diff --git a/build-fullstack.sh b/build-fullstack.sh
index f42879b..1e6d61a 100755
--- a/build-fullstack.sh
+++ b/build-fullstack.sh
@@ -148,7 +148,7 @@ build_frontend() {
# 构建
print_verbose "执行 SSG 构建..."
- if ! NEXT_EXPORT=true yarn build > build.log 2>&1; then
+ if ! NEXT_EXPORT=true NODE_ENV=production NEXT_PUBLIC_BACKEND_URL= NEXT_PUBLIC_WS_URL= NEXT_PUBLIC_API_BASE_URL= yarn build > build.log 2>&1; then
print_error "前端构建失败,查看 $FRONTEND_DIR/build.log"
cat build.log
# 恢复 API 目录后再退出
diff --git a/chuan-next/build-ssg.sh b/chuan-next/build-ssg.sh
index 88c5448..072e7ae 100644
--- a/chuan-next/build-ssg.sh
+++ b/chuan-next/build-ssg.sh
@@ -228,10 +228,10 @@ set_build_env() {
NEXT_EXPORT=true
NODE_ENV=production
-# 后端连接配置(用于静态模式)
-NEXT_PUBLIC_GO_BACKEND_URL=http://localhost:8080
-NEXT_PUBLIC_WS_URL=ws://localhost:8080/ws
-NEXT_PUBLIC_API_BASE_URL=http://localhost:8080
+# 后端连接配置(用于静态模式 - 使用相对路径)
+NEXT_PUBLIC_GO_BACKEND_URL=
+NEXT_PUBLIC_WS_URL=
+NEXT_PUBLIC_API_BASE_URL=
EOF
print_verbose "已创建 .env.ssg 文件"
@@ -252,9 +252,9 @@ run_static_build() {
# 设置环境变量并执行构建
if [ "$VERBOSE" = true ]; then
- NEXT_EXPORT=true NODE_ENV=production NEXT_PUBLIC_BACKEND_URL=http://localhost:8080 yarn build
+ NEXT_EXPORT=true NODE_ENV=production NEXT_PUBLIC_BACKEND_URL= yarn build
else
- NEXT_EXPORT=true NODE_ENV=production NEXT_PUBLIC_BACKEND_URL=http://localhost:8080 yarn build > build.log 2>&1
+ NEXT_EXPORT=true NODE_ENV=production NEXT_PUBLIC_BACKEND_URL= yarn build > build.log 2>&1
if [ $? -ne 0 ]; then
print_error "构建失败,查看 build.log 获取详细信息"
cat build.log
diff --git a/chuan-next/src/app/HomePage-new.tsx b/chuan-next/src/app/HomePage-new.tsx
index 770941d..bc6851f 100644
--- a/chuan-next/src/app/HomePage-new.tsx
+++ b/chuan-next/src/app/HomePage-new.tsx
@@ -1,6 +1,7 @@
"use client";
-import React from 'react';
+import React, { useEffect, useState } from 'react';
+import { useSearchParams } from 'next/navigation';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Upload, MessageSquare, Monitor, Github, ExternalLink } from 'lucide-react';
import Hero from '@/components/Hero';
@@ -10,6 +11,21 @@ import { WebRTCFileTransfer } from '@/components/WebRTCFileTransfer';
import { Button } from '@/components/ui/button';
export default function HomePage() {
+ const searchParams = useSearchParams();
+ const [activeTab, setActiveTab] = useState('webrtc');
+ const [hasInitialized, setHasInitialized] = useState(false);
+
+ // 根据URL参数设置初始标签(仅首次加载时)
+ useEffect(() => {
+ if (!hasInitialized) {
+ const urlType = searchParams.get('type');
+ if (urlType && ['webrtc', 'text', 'desktop'].includes(urlType)) {
+ setActiveTab(urlType);
+ }
+ setHasInitialized(true);
+ }
+ }, [searchParams, hasInitialized]);
+
return (
@@ -20,7 +36,7 @@ export default function HomePage() {
{/* Main Content */}
-
+
{/* Tabs Navigation - 横向布局 */}
diff --git a/chuan-next/src/app/api/webrtc-room-status/route.ts b/chuan-next/src/app/api/webrtc-room-status/route.ts
new file mode 100644
index 0000000..09086e1
--- /dev/null
+++ b/chuan-next/src/app/api/webrtc-room-status/route.ts
@@ -0,0 +1,38 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+const GO_BACKEND_URL = process.env.GO_BACKEND_URL || 'http://localhost:8080';
+
+export async function GET(request: NextRequest) {
+ try {
+ const { searchParams } = new URL(request.url);
+ const code = searchParams.get('code');
+
+ if (!code) {
+ return NextResponse.json(
+ { success: false, message: '取件码不能为空' },
+ { status: 400 }
+ );
+ }
+
+ console.log('API Route: Getting WebRTC room status, proxying to:', `${GO_BACKEND_URL}/api/webrtc-room-status?code=${code}`);
+
+ const response = await fetch(`${GO_BACKEND_URL}/api/webrtc-room-status?code=${code}`, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+
+ const data = await response.json();
+
+ console.log('Backend response:', response.status, data);
+
+ return NextResponse.json(data, { status: response.status });
+ } catch (error) {
+ console.error('API Route Error:', error);
+ return NextResponse.json(
+ { success: false, message: '获取房间状态失败', details: error instanceof Error ? error.message : 'Unknown error' },
+ { status: 500 }
+ );
+ }
+}
diff --git a/chuan-next/src/components/WebRTCFileTransfer.tsx b/chuan-next/src/components/WebRTCFileTransfer.tsx
index 0466a04..5929c77 100644
--- a/chuan-next/src/components/WebRTCFileTransfer.tsx
+++ b/chuan-next/src/components/WebRTCFileTransfer.tsx
@@ -36,6 +36,7 @@ export const WebRTCFileTransfer: React.FC = () => {
// 房间状态
const [pickupCode, setPickupCode] = useState('');
const [mode, setMode] = useState<'send' | 'receive'>('send');
+ const [hasProcessedInitialUrl, setHasProcessedInitialUrl] = useState(false);
const fileInputRef = useRef(null);
const {
@@ -54,27 +55,43 @@ export const WebRTCFileTransfer: React.FC = () => {
onFileProgress
} = useWebRTCTransfer();
- // 从URL参数中获取初始模式
+ // 从URL参数中获取初始模式(仅在首次加载时处理)
useEffect(() => {
const urlMode = searchParams.get('mode') as 'send' | 'receive';
const type = searchParams.get('type');
const code = searchParams.get('code');
- if (type === 'webrtc' && urlMode && ['send', 'receive'].includes(urlMode)) {
+ // 只在首次加载且URL中有webrtc类型时处理
+ if (!hasProcessedInitialUrl && type === 'webrtc' && urlMode && ['send', 'receive'].includes(urlMode)) {
+ console.log('=== 处理初始URL参数 ===');
+ console.log('URL模式:', urlMode, '类型:', type, '取件码:', code);
+
setMode(urlMode);
+ setHasProcessedInitialUrl(true);
+
if (code && urlMode === 'receive') {
- // 自动加入房间
+ // 自动加入房间,使用房间状态检查
+ console.log('URL中有取件码,自动加入房间');
joinRoom(code);
}
}
- }, [searchParams]);
+ }, [searchParams, hasProcessedInitialUrl]);
// 更新URL参数
const updateMode = useCallback((newMode: 'send' | 'receive') => {
+ console.log('=== 手动切换模式 ===');
+ console.log('新模式:', newMode);
+
setMode(newMode);
const params = new URLSearchParams(searchParams.toString());
params.set('type', 'webrtc');
params.set('mode', newMode);
+
+ // 如果切换到发送模式,移除code参数
+ if (newMode === 'send') {
+ params.delete('code');
+ }
+
router.push(`?${params.toString()}`, { scroll: false });
}, [searchParams, router]);
@@ -164,14 +181,46 @@ export const WebRTCFileTransfer: React.FC = () => {
};
// 加入房间 (接收模式)
- const joinRoom = (code: string) => {
+ const joinRoom = async (code: string) => {
console.log('=== 加入房间 ===');
console.log('取件码:', code);
- setPickupCode(code.trim());
- connect(code.trim(), 'receiver');
+ const trimmedCode = code.trim();
- showToast(`正在连接到房间: ${code}`, "info");
+ // 检查取件码格式
+ if (!trimmedCode || trimmedCode.length !== 6) {
+ showToast('请输入正确的6位取件码', "error");
+ return;
+ }
+
+ try {
+ // 先检查房间状态
+ console.log('检查房间状态...');
+ showToast('正在检查房间状态...', "info");
+
+ const response = await fetch(`/api/webrtc-room-status?code=${trimmedCode}`);
+ const result = await response.json();
+
+ if (!result.success) {
+ showToast(result.message || '房间不存在或已过期', "error");
+ return;
+ }
+
+ // 检查发送方是否在线
+ if (!result.sender_online) {
+ showToast('发送方不在线,请确认取件码是否正确', "error");
+ return;
+ }
+
+ console.log('房间状态检查通过,开始连接...');
+ setPickupCode(trimmedCode);
+ connect(trimmedCode, 'receiver');
+
+ showToast(`正在连接到房间: ${trimmedCode}`, "info");
+ } catch (error) {
+ console.error('检查房间状态失败:', error);
+ showToast('检查房间状态失败,请重试', "error");
+ }
};
// 重置连接状态 (用于连接失败后重新输入)
diff --git a/chuan-next/src/hooks/useFileTransfer.ts b/chuan-next/src/hooks/useFileTransfer.ts
index 2ca61ee..7f25827 100644
--- a/chuan-next/src/hooks/useFileTransfer.ts
+++ b/chuan-next/src/hooks/useFileTransfer.ts
@@ -1,5 +1,5 @@
-import { useState, useCallback, useRef } from 'react';
-import { FileInfo, TransferProgress } from '@/types';
+import { useState, useCallback } from 'react';
+import { TransferProgress } from '@/types';
import { useToast } from '@/components/ui/toast-simple';
interface FileTransferData {
diff --git a/chuan-next/src/hooks/useWebRTCTransfer.ts b/chuan-next/src/hooks/useWebRTCTransfer.ts
index be07b30..b29e14d 100644
--- a/chuan-next/src/hooks/useWebRTCTransfer.ts
+++ b/chuan-next/src/hooks/useWebRTCTransfer.ts
@@ -20,9 +20,9 @@ export function useWebRTCTransfer() {
// 设置数据通道消息处理
useEffect(() => {
- const dataChannel = connection.getDataChannel();
+ const dataChannel = connection.localDataChannel || connection.remoteDataChannel;
if (dataChannel && dataChannel.readyState === 'open') {
- console.log('设置数据通道消息处理器');
+ console.log('设置数据通道消息处理器, 通道类型:', connection.localDataChannel ? '本地' : '远程');
// 扩展消息处理以包含文件列表
const originalHandler = fileTransfer.handleMessage;
@@ -50,7 +50,7 @@ export function useWebRTCTransfer() {
originalHandler(event);
};
}
- }, [connection.isConnected, connection.getDataChannel, fileTransfer.handleMessage]);
+ }, [connection.localDataChannel, connection.remoteDataChannel, fileTransfer.handleMessage]);
// 发送文件
const sendFile = useCallback((file: File, fileId?: string) => {
diff --git a/chuan-next/src/hooks/webrtc/useWebRTCConnection.ts b/chuan-next/src/hooks/webrtc/useWebRTCConnection.ts
index 447ba04..4d9c22d 100644
--- a/chuan-next/src/hooks/webrtc/useWebRTCConnection.ts
+++ b/chuan-next/src/hooks/webrtc/useWebRTCConnection.ts
@@ -1,280 +1,810 @@
-import { url } from 'inspector';
import { useState, useRef, useCallback } from 'react';
+import { config } from '@/lib/config';
interface WebRTCConnectionState {
- isConnected: boolean;
- isConnecting: boolean;
- isWebSocketConnected: boolean;
- error: string | null;
- localDataChannel: RTCDataChannel | null;
- remoteDataChannel: RTCDataChannel | null;
+ isConnected: boolean;
+ isConnecting: boolean;
+ isWebSocketConnected: boolean;
+ error: string | null;
+ localDataChannel: RTCDataChannel | null;
+ remoteDataChannel: RTCDataChannel | null;
}
export function useWebRTCConnection() {
- const [state, setState] = useState({
- isConnected: false,
- isConnecting: false,
- isWebSocketConnected: false,
- error: null,
- localDataChannel: null,
- remoteDataChannel: null,
- });
+ const [state, setState] = useState({
+ isConnected: false,
+ isConnecting: false,
+ isWebSocketConnected: false,
+ error: null,
+ localDataChannel: null,
+ remoteDataChannel: null,
+ });
- const wsRef = useRef(null);
- const pcRef = useRef(null);
- const dcRef = useRef(null);
- const timeoutRef = useRef(null);
+ // 浏览器兼容性检测
+ const detectBrowser = useCallback(() => {
+ const userAgent = navigator.userAgent;
+ const isEdge = /Edg/.test(userAgent);
+ const isChrome = /Chrome/.test(userAgent) && !isEdge;
+ const isSafari = /Safari/.test(userAgent) && !isChrome && !isEdge;
+ const isFirefox = /Firefox/.test(userAgent);
+ const isChromeFamily = isChrome || isEdge; // Chrome内核系列
+
+ console.log('浏览器检测结果:', {
+ userAgent: userAgent.substring(0, 100) + '...',
+ isEdge,
+ isChrome,
+ isSafari,
+ isFirefox,
+ isChromeFamily,
+ webRTCSupport: {
+ RTCPeerConnection: !!window.RTCPeerConnection,
+ getUserMedia: !!(navigator.mediaDevices?.getUserMedia),
+ WebSocket: !!window.WebSocket
+ }
+ });
+
+ return { isEdge, isChrome, isSafari, isFirefox, isChromeFamily };
+ }, []);
+
+ const wsRef = useRef(null);
+ const pcRef = useRef(null);
+ const dcRef = useRef(null);
+ const timeoutRef = useRef(null);
+ const pendingIceCandidates = useRef([]);
+ const iceGatheringTimeoutRef = useRef(null);
const STUN_SERVERS = [
+ // Edge 浏览器专用优化配置
{ urls: 'stun:stun.l.google.com:19302' },
{ urls: 'stun:stun1.l.google.com:19302' },
{ urls: 'stun:stun2.l.google.com:19302' },
+ { urls: 'stun:stun3.l.google.com:19302' },
+ { urls: 'stun:stun4.l.google.com:19302' },
+
+ // 备用 STUN 服务器
+ { urls: 'stun:stun.nextcloud.com:443' },
+ { urls: 'stun:stun.sipgate.net:10000' },
+ { urls: 'stun:stun.ekiga.net' },
];
+ // 获取浏览器特定的 RTCConfiguration
+ const getBrowserSpecificConfig = useCallback(() => {
+ const { isEdge, isSafari, isChromeFamily } = detectBrowser();
+
+ const baseConfig = {
+ iceServers: STUN_SERVERS,
+ iceCandidatePoolSize: 10,
+ bundlePolicy: 'max-bundle' as RTCBundlePolicy,
+ rtcpMuxPolicy: 'require' as RTCRtcpMuxPolicy,
+ iceTransportPolicy: 'all' as RTCIceTransportPolicy
+ };
+
+ if (isChromeFamily) {
+ console.log('应用 Chrome 内核浏览器优化配置');
+ return {
+ ...baseConfig,
+ // Chrome 内核特定优化
+ iceCandidatePoolSize: 20, // 增加候选池大小
+ bundlePolicy: 'max-bundle' as RTCBundlePolicy,
+ rtcpMuxPolicy: 'require' as RTCRtcpMuxPolicy,
+ iceTransportPolicy: 'all' as RTCIceTransportPolicy,
+ // Chrome 内核需要更宽松的配置
+ sdpSemantics: 'unified-plan' as const, // 明确使用统一计划
+ };
+ }
+
+ if (isSafari) {
+ console.log('应用 Safari 浏览器优化配置');
+ return {
+ ...baseConfig,
+ // Safari 特定优化
+ iceCandidatePoolSize: 8,
+ sdpSemantics: 'unified-plan' as const,
+ };
+ }
+
+ console.log('应用默认浏览器配置');
+ return baseConfig;
+ }, [detectBrowser, STUN_SERVERS]);
+
// 连接超时时间(30秒)
const CONNECTION_TIMEOUT = 30000;
- const updateState = useCallback((updates: Partial) => {
- setState(prev => ({ ...prev, ...updates }));
- }, []);
+ const updateState = useCallback((updates: Partial) => {
+ setState(prev => ({ ...prev, ...updates }));
+ }, []);
- // 清理超时定时器
- const clearConnectionTimeout = useCallback(() => {
- if (timeoutRef.current) {
- clearTimeout(timeoutRef.current);
- timeoutRef.current = null;
- }
- }, []);
+ // 处理缓存的ICE候选
+ const processPendingIceCandidates = useCallback(async () => {
+ const pc = pcRef.current;
+ if (!pc || !pc.remoteDescription || pendingIceCandidates.current.length === 0) {
+ return;
+ }
- // 处理连接超时
- const handleConnectionTimeout = useCallback(() => {
- console.warn('WebRTC连接超时');
- updateState({
- error: '连接超时,请检查取件码是否正确或稍后重试',
- isConnecting: false
- });
-
- // 清理连接
- if (wsRef.current) {
- wsRef.current.close();
- }
- if (pcRef.current) {
- pcRef.current.close();
- }
- }, [updateState]);
-
- const connect = useCallback(async (roomCode: string, role: 'sender' | 'receiver') => {
- console.log('=== 开始WebRTC连接 ===');
- console.log('房间代码:', roomCode, '角色:', role);
-
- // 清理之前的超时定时器
- clearConnectionTimeout();
-
- updateState({ isConnecting: true, error: null });
-
- // 设置连接超时
- timeoutRef.current = setTimeout(() => {
- handleConnectionTimeout();
- }, CONNECTION_TIMEOUT);
-
- try {
- // 创建PeerConnection
- const pc = new RTCPeerConnection({ iceServers: STUN_SERVERS });
- pcRef.current = pc;
-
- // 连接WebSocket信令服务器
- const ws = new WebSocket(`ws://localhost:8080/ws/webrtc?code=${roomCode}&role=${role}`);
- wsRef.current = ws;
-
- // WebSocket事件处理
- ws.onopen = async () => {
- console.log('WebSocket连接已建立');
- updateState({ isWebSocketConnected: true });
+ console.log('处理缓存的ICE候选,数量:', pendingIceCandidates.current.length);
- // 如果是发送方,在WebSocket连接建立后创建offer
- if (role === 'sender') {
- try {
- const offer = await pc.createOffer();
+ for (const candidate of pendingIceCandidates.current) {
+ try {
+ await pc.addIceCandidate(candidate);
+ console.log('已添加缓存的ICE候选');
+ } catch (error) {
+ console.warn('添加缓存ICE候选失败:', error);
+ }
+ }
+
+ // 清空缓存
+ pendingIceCandidates.current = [];
+ }, []);
+
+ // 优化的Offer创建和发送
+ const createAndSendOffer = useCallback(async (pc: RTCPeerConnection, ws: WebSocket) => {
+ console.log('开始创建offer...');
+
+ // 获取浏览器信息
+ const browserInfo = detectBrowser();
+
+ try {
+ // Chrome内核需要特殊的offer配置
+ const offerOptions = browserInfo.isChromeFamily ? {
+ offerToReceiveAudio: false,
+ offerToReceiveVideo: false,
+ iceRestart: false,
+ // Chrome内核特定配置
+ voiceActivityDetection: false
+ } : {
+ offerToReceiveAudio: false,
+ offerToReceiveVideo: false,
+ iceRestart: false
+ };
+
+ console.log('使用的 offer 配置:', offerOptions);
+
+ // 创建offer
+ const offer = await pc.createOffer(offerOptions);
+
await pc.setLocalDescription(offer);
- ws.send(JSON.stringify({ type: 'offer', payload: offer }));
- console.log('已发送offer');
- } catch (error) {
+ console.log('已设置本地描述,等待ICE候选收集...');
+
+ // Chrome内核需要更长的ICE收集时间
+ const iceGatheringTimeout = browserInfo.isChromeFamily ? 5000 : 3000;
+
+ // 设置ICE收集超时 - 等待更多ICE候选
+ const iceTimeout = setTimeout(() => {
+ console.log('ICE收集超时,发送当前offer');
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify({ type: 'offer', payload: pc.localDescription }));
+ console.log('已发送offer (超时发送)');
+ }
+ }, iceGatheringTimeout);
+
+ iceGatheringTimeoutRef.current = iceTimeout;
+
+ // 监听ICE收集完成
+ const handleIceGatheringComplete = () => {
+ if (iceGatheringTimeoutRef.current) {
+ clearTimeout(iceGatheringTimeoutRef.current);
+ iceGatheringTimeoutRef.current = null;
+ }
+
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify({ type: 'offer', payload: pc.localDescription }));
+ const candidateCount = pc.localDescription?.sdp ?
+ pc.localDescription.sdp.split('a=candidate:').length - 1 : 0;
+ console.log('已发送offer (ICE收集完成)', '候选数量:', candidateCount);
+ }
+ };
+
+ // 检查ICE收集状态
+ if (pc.iceGatheringState === 'complete') {
+ handleIceGatheringComplete();
+ } else {
+ // 监听ICE收集状态变化
+ const originalHandler = pc.onicegatheringstatechange;
+ pc.onicegatheringstatechange = (event) => {
+ console.log('ICE收集状态变化:', pc.iceGatheringState);
+ if (originalHandler) originalHandler.call(pc, event);
+
+ if (pc.iceGatheringState === 'complete') {
+ handleIceGatheringComplete();
+ // 恢复原始处理器
+ pc.onicegatheringstatechange = originalHandler;
+ }
+ };
+ }
+
+ } catch (error) {
console.error('创建offer失败:', error);
updateState({ error: '创建连接失败', isConnecting: false });
- }
}
- };
-
- ws.onmessage = async (event) => {
- try {
- const message = JSON.parse(event.data);
- console.log('收到信令消息:', message);
-
- switch (message.type) {
- case 'offer':
- if (message.payload) {
- await pc.setRemoteDescription(new RTCSessionDescription(message.payload));
- const answer = await pc.createAnswer();
- await pc.setLocalDescription(answer);
- if (ws.readyState === WebSocket.OPEN) {
- ws.send(JSON.stringify({ type: 'answer', payload: answer }));
- console.log('已发送answer');
- }
- }
- break;
-
- case 'answer':
- if (message.payload) {
- await pc.setRemoteDescription(new RTCSessionDescription(message.payload));
- }
- break;
-
- case 'ice-candidate':
- if (message.payload) {
- await pc.addIceCandidate(new RTCIceCandidate(message.payload));
- }
- break;
-
- case 'error':
- console.error('信令错误:', message.error);
- updateState({ error: message.error, isConnecting: false });
- break;
- }
- } catch (error) {
- console.error('处理信令消息失败:', error);
- updateState({ error: '信令处理失败', isConnecting: false });
- }
- };
-
- ws.onerror = (error) => {
- console.error('WebSocket错误:', error);
- updateState({ error: 'WebSocket连接失败', isConnecting: false, isWebSocketConnected: false });
- };
-
- ws.onclose = () => {
- console.log('WebSocket连接已关闭');
- updateState({ isWebSocketConnected: false });
- };
-
- // ICE候选事件
- pc.onicecandidate = (event) => {
- if (event.candidate && ws.readyState === WebSocket.OPEN) {
- console.log('发送ICE候选:', event.candidate);
- ws.send(JSON.stringify({
- type: 'ice-candidate',
- payload: event.candidate
- }));
- }
- };
-
- // 连接状态变化
- pc.onconnectionstatechange = () => {
- console.log('连接状态:', pc.connectionState);
- const isConnected = pc.connectionState === 'connected';
-
- if (isConnected) {
- // 连接成功,清理超时定时器
- clearConnectionTimeout();
- }
-
- updateState({
- isConnected,
- isConnecting: !isConnected && pc.connectionState !== 'failed'
- });
-
- if (pc.connectionState === 'failed') {
- clearConnectionTimeout();
- updateState({ error: '连接失败', isConnecting: false });
- }
- };
-
- // 如果是发送方,创建数据通道
- if (role === 'sender') {
- const dataChannel = pc.createDataChannel('fileTransfer', {
- ordered: true,
- maxPacketLifeTime: undefined,
- maxRetransmits: undefined
- });
- dcRef.current = dataChannel;
-
- // 设置缓冲区管理
- dataChannel.bufferedAmountLowThreshold = 256 * 1024; // 256KB
-
- dataChannel.onopen = () => {
- console.log('数据通道已打开 (发送方)');
- console.log('数据通道配置:', {
- id: dataChannel.id,
- label: dataChannel.label,
- maxPacketLifeTime: dataChannel.maxPacketLifeTime,
- maxRetransmits: dataChannel.maxRetransmits,
- ordered: dataChannel.ordered,
- bufferedAmountLowThreshold: dataChannel.bufferedAmountLowThreshold
- });
- updateState({ localDataChannel: dataChannel });
- };
- } else {
- // 接收方等待数据通道
- pc.ondatachannel = (event) => {
- const dataChannel = event.channel;
- dcRef.current = dataChannel;
- console.log('收到数据通道 (接收方)');
-
- dataChannel.onopen = () => {
- console.log('数据通道已打开 (接收方)');
- updateState({ remoteDataChannel: dataChannel });
- };
- };
- }
-
- } catch (error) {
- console.error('连接失败:', error);
- clearConnectionTimeout();
- updateState({
- error: error instanceof Error ? error.message : '连接失败',
- isConnecting: false
- });
- }
- }, [updateState, clearConnectionTimeout, handleConnectionTimeout]);
-
- const disconnect = useCallback(() => {
- console.log('断开WebRTC连接');
+ }, [updateState, detectBrowser]);
// 清理超时定时器
- clearConnectionTimeout();
+ const clearConnectionTimeout = useCallback(() => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ timeoutRef.current = null;
+ }
+ }, []);
- if (dcRef.current) {
- dcRef.current.close();
- dcRef.current = null;
- }
+ // 处理连接超时
+ const handleConnectionTimeout = useCallback(() => {
+ console.warn('WebRTC连接超时');
- if (pcRef.current) {
- pcRef.current.close();
- pcRef.current = null;
- }
+ // 获取当前连接状态用于调试
+ const pc = pcRef.current;
+ const connectionInfo = {
+ connectionState: pc?.connectionState || 'unknown',
+ iceConnectionState: pc?.iceConnectionState || 'unknown',
+ signalingState: pc?.signalingState || 'unknown',
+ isWebSocketConnected: wsRef.current?.readyState === WebSocket.OPEN
+ };
- if (wsRef.current) {
- wsRef.current.close();
- wsRef.current = null;
- }
+ console.log('连接超时时的状态:', connectionInfo);
- setState({
- isConnected: false,
- isConnecting: false,
- isWebSocketConnected: false,
- error: null,
- localDataChannel: null,
- remoteDataChannel: null,
- });
- }, [clearConnectionTimeout]);
+ updateState({
+ error: `连接超时 - WebSocket: ${connectionInfo.isWebSocketConnected ? '已连接' : '未连接'}, 信令状态: ${connectionInfo.signalingState}, 连接状态: ${connectionInfo.connectionState}`,
+ isConnecting: false
+ });
- const getDataChannel = useCallback(() => {
- return dcRef.current;
- }, []);
+ // 清理连接
+ if (wsRef.current) {
+ wsRef.current.close();
+ }
+ if (pcRef.current) {
+ pcRef.current.close();
+ }
+ }, [updateState]);
- return {
- ...state,
- connect,
- disconnect,
- getDataChannel,
- };
+ const connect = useCallback(async (roomCode: string, role: 'sender' | 'receiver') => {
+ console.log('=== 开始WebRTC连接 ===');
+ console.log('房间代码:', roomCode, '角色:', role);
+
+ // 浏览器兼容性检测
+ const browserInfo = detectBrowser();
+ console.log('当前浏览器:', browserInfo);
+
+ // 清理之前的超时定时器
+ clearConnectionTimeout();
+
+ updateState({ isConnecting: true, error: null });
+
+ // Chrome内核浏览器使用更长的超时时间
+ const timeoutDuration = browserInfo.isChromeFamily ? CONNECTION_TIMEOUT * 2 : CONNECTION_TIMEOUT;
+
+ // 只有接收方设置连接超时,发送方无限等待
+ if (role === 'receiver') {
+ timeoutRef.current = setTimeout(() => {
+ handleConnectionTimeout();
+ }, timeoutDuration);
+ }
+
+ try {
+ // 获取浏览器特定的配置
+ const rtcConfig = getBrowserSpecificConfig();
+ console.log('使用的 RTCConfiguration:', rtcConfig);
+
+ // 创建PeerConnection
+ const pc = new RTCPeerConnection(rtcConfig);
+ pcRef.current = pc;
+
+ // 连接WebSocket信令服务器
+ const wsUrl = config.api.wsUrl.replace('/ws/p2p', '/ws/webrtc');
+ const finalWsUrl = `${wsUrl}?code=${roomCode}&role=${role}`;
+
+ console.log('WebSocket 连接信息:', {
+ 原始wsUrl: config.api.wsUrl,
+ 替换后wsUrl: wsUrl,
+ 最终URL: finalWsUrl,
+ 当前域名: typeof window !== 'undefined' ? window.location.host : 'unknown',
+ 协议: typeof window !== 'undefined' ? window.location.protocol : 'unknown',
+ 端口: typeof window !== 'undefined' ? window.location.port : 'unknown',
+ 浏览器: browserInfo
+ });
+
+ const ws = new WebSocket(finalWsUrl);
+ wsRef.current = ws;
+
+ // Chrome内核特殊处理:增加连接超时检测
+ let wsConnectTimeout: NodeJS.Timeout | null = null;
+
+ if (browserInfo.isChromeFamily) {
+ wsConnectTimeout = setTimeout(() => {
+ if (ws.readyState === WebSocket.CONNECTING) {
+ console.error('Chrome内核 - WebSocket连接超时');
+ ws.close();
+ updateState({
+ error: 'WebSocket连接超时 - Chrome内核可能存在安全策略限制',
+ isConnecting: false
+ });
+ }
+ }, 10000); // 10秒超时
+ }
+
+ // WebSocket事件处理
+ ws.onopen = async () => {
+ console.log('WebSocket连接已建立,URL:', finalWsUrl);
+
+ // 清理Chrome内核的连接超时
+ if (wsConnectTimeout) {
+ clearTimeout(wsConnectTimeout);
+ wsConnectTimeout = null;
+ }
+
+ updateState({ isWebSocketConnected: true });
+
+ // 如果是发送方,在WebSocket连接建立后创建offer
+ if (role === 'sender') {
+ // 使用优化的offer创建逻辑
+ createAndSendOffer(pc, ws);
+ // 发送方发送 offer 后,停止 connecting 状态
+ updateState({ isConnecting: false });
+ }
+ };
+
+ ws.onmessage = async (event) => {
+ try {
+ const message = JSON.parse(event.data);
+ console.log('收到信令消息:', message, '当前PC状态:', pc.signalingState);
+
+ switch (message.type) {
+ case 'offer':
+ if (message.payload) {
+ console.log('处理offer,当前状态:', pc.signalingState);
+ try {
+ // 根据W3C规范,只有在stable状态下才能接收offer
+ if (pc.signalingState !== 'stable') {
+ console.warn('跳过offer,信令状态不为stable:', pc.signalingState);
+ return;
+ }
+
+ await pc.setRemoteDescription(new RTCSessionDescription(message.payload));
+ console.log('已设置远程描述,状态变为:', pc.signalingState);
+
+ // 创建answer
+ const answer = await pc.createAnswer();
+ await pc.setLocalDescription(answer);
+ console.log('已创建并设置本地answer,状态变为:', pc.signalingState);
+
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify({ type: 'answer', payload: answer }));
+ console.log('已发送answer');
+ }
+
+ // 接收方发送answer后,停止connecting状态
+ updateState({ isConnecting: false });
+
+ // 处理缓存的ICE候选
+ await processPendingIceCandidates();
+ } catch (error) {
+ console.error('处理offer失败:', error);
+ updateState({ error: '信令交换失败', isConnecting: false });
+ }
+ }
+ break;
+
+ case 'answer':
+ if (message.payload) {
+ console.log('处理answer,当前状态:', pc.signalingState);
+ try {
+ // 根据W3C规范,只有在have-local-offer状态下才能接收answer
+ if (pc.signalingState !== 'have-local-offer') {
+ console.warn('跳过answer,信令状态不为have-local-offer:', pc.signalingState);
+ return;
+ }
+
+ await pc.setRemoteDescription(new RTCSessionDescription(message.payload));
+ console.log('信令交换完成,状态变为:', pc.signalingState);
+
+ // 处理缓存的ICE候选
+ await processPendingIceCandidates();
+ } catch (error) {
+ console.error('处理answer失败:', error);
+ updateState({ error: '信令交换失败', isConnecting: false });
+ }
+ }
+ break;
+
+ case 'ice-candidate':
+ if (message.payload) {
+ try {
+ const candidate = new RTCIceCandidate(message.payload);
+
+ // 根据W3C规范,检查连接状态
+ if (pc.signalingState === 'closed') {
+ console.warn('跳过ICE候选,连接已关闭');
+ return;
+ }
+
+ // 如果有远程描述,直接添加ICE候选
+ if (pc.remoteDescription) {
+ await pc.addIceCandidate(candidate);
+ console.log('已添加ICE候选:', {
+ type: candidate.type,
+ protocol: candidate.protocol,
+ address: candidate.address?.substring(0, 10) + '...',
+ port: candidate.port
+ });
+ } else {
+ // 缓存ICE候选,等待远程描述设置后处理
+ pendingIceCandidates.current.push(candidate);
+ console.log('缓存ICE候选,等待远程描述:', {
+ type: candidate.type,
+ 缓存数量: pendingIceCandidates.current.length
+ });
+ }
+ } catch (error) {
+ console.warn('处理ICE候选失败:', error);
+ // 根据W3C规范,ICE候选错误不应该终止连接
+ }
+ }
+ break;
+
+ case 'disconnection':
+ console.log('收到断开连接通知:', message.payload);
+ const disconnectionMessage = message.payload?.message || '对方已停止传输';
+ updateState({
+ error: disconnectionMessage,
+ isConnecting: false,
+ isConnected: false,
+ isWebSocketConnected: false
+ });
+ // 关闭WebSocket连接
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.close(1000, '对方已断开连接');
+ }
+ // 关闭WebRTC连接
+ if (pc.connectionState !== 'closed') {
+ pc.close();
+ }
+ break;
+
+ case 'error':
+ console.error('信令错误:', message.error);
+ updateState({ error: message.error, isConnecting: false });
+ break;
+ }
+ } catch (error) {
+ console.error('处理信令消息失败:', error);
+ updateState({ error: '信令处理失败', isConnecting: false });
+ }
+ };
+
+ ws.onerror = (error) => {
+ console.error('WebSocket错误:', error);
+ console.error('WebSocket连接失败,URL:', finalWsUrl);
+
+ // 清理Chrome内核的连接超时
+ if (wsConnectTimeout) {
+ clearTimeout(wsConnectTimeout);
+ wsConnectTimeout = null;
+ }
+
+ // Chrome内核特殊错误处理
+ const errorMessage = browserInfo.isChromeFamily
+ ? 'WebSocket连接失败 - Chrome内核可能阻止了不安全的连接,请确保使用HTTPS'
+ : 'WebSocket连接失败';
+
+ updateState({
+ error: errorMessage,
+ isConnecting: false,
+ isWebSocketConnected: false
+ });
+ };
+
+ ws.onclose = (event) => {
+ console.log('WebSocket连接已关闭,代码:', event.code, '原因:', event.reason, 'URL:', finalWsUrl);
+ updateState({ isWebSocketConnected: false });
+ };
+
+ // ICE候选事件 - 增强处理,Edge浏览器特殊优化
+ pc.onicecandidate = (event) => {
+ if (event.candidate) {
+ const candidateInfo = {
+ type: event.candidate.type,
+ protocol: event.candidate.protocol,
+ address: event.candidate.address,
+ port: event.candidate.port,
+ priority: event.candidate.priority,
+ foundation: event.candidate.foundation
+ };
+
+ console.log('ICE候选信息:', candidateInfo);
+
+ // Chrome内核浏览器特殊处理:检查候选质量和延迟
+ if (browserInfo.isChromeFamily && event.candidate.priority !== null) {
+ // Chrome内核可能生成质量较低的候选,添加延迟来等待更好的候选
+ const isLowQualityCandidate = event.candidate.type === 'host' &&
+ event.candidate.priority < 1000000;
+
+ if (isLowQualityCandidate) {
+ console.log('Chrome内核 - 延迟发送低质量候选');
+ setTimeout(() => {
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify({
+ type: 'ice-candidate',
+ payload: event.candidate
+ }));
+ }
+ }, 800); // Chrome内核需要更长延迟
+ return;
+ }
+ }
+
+ if (ws.readyState === WebSocket.OPEN) {
+ ws.send(JSON.stringify({
+ type: 'ice-candidate',
+ payload: event.candidate
+ }));
+ }
+ } else {
+ console.log('ICE候选收集完成');
+ }
+ };
+
+ // 添加ICE收集状态监听
+ pc.onicegatheringstatechange = () => {
+ console.log('ICE收集状态变化:', pc.iceGatheringState);
+ };
+
+ // 信令状态变化监听 - 增强版
+ pc.onsignalingstatechange = () => {
+ console.log('信令状态变化:', pc.signalingState);
+
+ // 根据W3C规范验证状态转换
+ switch (pc.signalingState) {
+ case 'stable':
+ console.log('信令协商完成,连接稳定');
+ break;
+ case 'have-local-offer':
+ console.log('已发送offer,等待answer');
+ break;
+ case 'have-remote-offer':
+ console.log('已接收offer,需要发送answer');
+ break;
+ case 'have-local-pranswer':
+ console.log('已发送provisional answer');
+ break;
+ case 'have-remote-pranswer':
+ console.log('已接收provisional answer');
+ break;
+ case 'closed':
+ console.log('连接已关闭');
+ break;
+ default:
+ console.warn('未知的信令状态:', pc.signalingState);
+ }
+ };
+
+ // 连接状态变化 - 根据W3C规范
+ pc.onconnectionstatechange = () => {
+ console.log('连接状态变化:', pc.connectionState);
+
+ switch (pc.connectionState) {
+ case 'new':
+ console.log('连接初始化');
+ break;
+ case 'connecting':
+ console.log('正在建立连接...');
+ // 只有在当前不是已连接状态时才设置为连接中
+ if (!state.isConnected) {
+ updateState({ isConnecting: true });
+ }
+ break;
+ case 'connected':
+ console.log('连接已建立');
+ clearConnectionTimeout();
+ updateState({
+ isConnected: true,
+ isConnecting: false
+ });
+ break;
+ case 'disconnected':
+ console.log('连接已断开');
+ updateState({
+ isConnected: false
+ });
+ break;
+ case 'failed':
+ console.log('连接失败');
+ clearConnectionTimeout();
+ updateState({
+ error: '连接失败',
+ isConnecting: false,
+ isConnected: false
+ });
+ break;
+ case 'closed':
+ console.log('连接已关闭');
+ updateState({
+ isConnected: false,
+ isConnecting: false
+ });
+ break;
+ }
+ };
+
+ // ICE连接状态变化 - 根据W3C规范
+ pc.oniceconnectionstatechange = () => {
+ console.log('ICE连接状态变化:', pc.iceConnectionState);
+
+ switch (pc.iceConnectionState) {
+ case 'new':
+ console.log('ICE连接初始化');
+ break;
+ case 'checking':
+ console.log('ICE正在检查连通性...');
+ break;
+ case 'connected':
+ console.log('ICE连接成功');
+ clearConnectionTimeout();
+ break;
+ case 'completed':
+ console.log('ICE连接完成');
+ clearConnectionTimeout();
+ break;
+ case 'disconnected':
+ console.log('ICE连接断开');
+ updateState({
+ error: 'ICE连接断开',
+ isConnected: false
+ });
+ break;
+ case 'failed':
+ console.log('ICE连接失败');
+ clearConnectionTimeout();
+ updateState({
+ error: 'ICE连接失败',
+ isConnecting: false,
+ isConnected: false
+ });
+ break;
+ case 'closed':
+ console.log('ICE连接已关闭');
+ break;
+ }
+ };
+
+ // 如果是发送方,创建数据通道
+ if (role === 'sender') {
+ // 根据浏览器优化数据通道配置
+ const dataChannelConfig = browserInfo.isChromeFamily ? {
+ ordered: true,
+ maxPacketLifeTime: undefined,
+ maxRetransmits: undefined,
+ // Chrome内核特定配置
+ negotiated: false,
+ id: undefined,
+ protocol: '' // Chrome内核需要明确指定协议
+ } : {
+ ordered: true,
+ maxPacketLifeTime: undefined,
+ maxRetransmits: undefined
+ };
+
+ console.log('创建数据通道,配置:', dataChannelConfig);
+
+ // 根据W3C规范,数据通道应该在设置本地描述之前创建
+ const dataChannel = pc.createDataChannel('fileTransfer', dataChannelConfig);
+ dcRef.current = dataChannel;
+
+ // 设置缓冲区管理
+ dataChannel.bufferedAmountLowThreshold = 256 * 1024; // 256KB
+
+ dataChannel.onopen = () => {
+ console.log('数据通道已打开 (发送方)');
+ // 数据通道成功打开,清除超时定时器
+ clearConnectionTimeout();
+ console.log('数据通道配置:', {
+ id: dataChannel.id,
+ label: dataChannel.label,
+ maxPacketLifeTime: dataChannel.maxPacketLifeTime,
+ maxRetransmits: dataChannel.maxRetransmits,
+ ordered: dataChannel.ordered,
+ bufferedAmountLowThreshold: dataChannel.bufferedAmountLowThreshold,
+ readyState: dataChannel.readyState
+ });
+ updateState({ localDataChannel: dataChannel });
+ };
+
+ dataChannel.onclose = () => {
+ console.log('数据通道已关闭 (发送方)');
+ updateState({ localDataChannel: null });
+ };
+
+ dataChannel.onerror = (error) => {
+ console.error('数据通道错误 (发送方):', error);
+ updateState({ error: '数据通道连接失败', isConnecting: false });
+ };
+ } else {
+ // 接收方等待数据通道
+ pc.ondatachannel = (event) => {
+ const dataChannel = event.channel;
+ dcRef.current = dataChannel;
+ console.log('收到数据通道 (接收方),标签:', dataChannel.label);
+
+ dataChannel.onopen = () => {
+ console.log('数据通道已打开 (接收方)');
+ // 数据通道成功打开,清除超时定时器
+ clearConnectionTimeout();
+ console.log('数据通道配置:', {
+ id: dataChannel.id,
+ label: dataChannel.label,
+ readyState: dataChannel.readyState
+ });
+ updateState({ remoteDataChannel: dataChannel });
+ };
+
+ dataChannel.onclose = () => {
+ console.log('数据通道已关闭 (接收方)');
+ updateState({ remoteDataChannel: null });
+ };
+
+ dataChannel.onerror = (error) => {
+ console.error('数据通道错误 (接收方):', error);
+ updateState({ error: '数据通道连接失败', isConnecting: false });
+ };
+ };
+ }
+
+ } catch (error) {
+ console.error('连接失败:', error);
+ clearConnectionTimeout();
+ updateState({
+ error: error instanceof Error ? error.message : '连接失败',
+ isConnecting: false
+ });
+ }
+ }, [updateState, clearConnectionTimeout, handleConnectionTimeout, processPendingIceCandidates, createAndSendOffer, detectBrowser, getBrowserSpecificConfig]);
+
+ const disconnect = useCallback(() => {
+ console.log('断开WebRTC连接');
+
+ // 清理超时定时器
+ clearConnectionTimeout();
+
+ // 清理ICE收集超时
+ if (iceGatheringTimeoutRef.current) {
+ clearTimeout(iceGatheringTimeoutRef.current);
+ iceGatheringTimeoutRef.current = null;
+ console.log('已清理ICE收集超时定时器');
+ }
+
+ // 清理缓存的ICE候选
+ pendingIceCandidates.current = [];
+
+ if (dcRef.current) {
+ dcRef.current.close();
+ dcRef.current = null;
+ }
+
+ if (pcRef.current) {
+ pcRef.current.close();
+ pcRef.current = null;
+ }
+
+ if (wsRef.current) {
+ wsRef.current.close();
+ wsRef.current = null;
+ }
+
+ setState({
+ isConnected: false,
+ isConnecting: false,
+ isWebSocketConnected: false,
+ error: null,
+ localDataChannel: null,
+ remoteDataChannel: null,
+ });
+ }, [clearConnectionTimeout]);
+
+ const getDataChannel = useCallback(() => {
+ return dcRef.current;
+ }, []);
+
+ return {
+ ...state,
+ connect,
+ disconnect,
+ getDataChannel,
+ };
}
diff --git a/chuan-next/src/lib/client-api.ts b/chuan-next/src/lib/client-api.ts
index 5c411b6..6dc9026 100644
--- a/chuan-next/src/lib/client-api.ts
+++ b/chuan-next/src/lib/client-api.ts
@@ -3,7 +3,7 @@
* 用于在静态导出模式下直接与 Go 后端通信
*/
-import { config, getApiUrl, getDirectBackendUrl } from './config';
+import { config } from './config';
interface ApiResponse {
success: boolean;
diff --git a/chuan-next/src/lib/config.ts b/chuan-next/src/lib/config.ts
index 0b86318..476c216 100644
--- a/chuan-next/src/lib/config.ts
+++ b/chuan-next/src/lib/config.ts
@@ -27,11 +27,16 @@ const getCurrentBaseUrl = () => {
// 动态获取 WebSocket URL
const getCurrentWsUrl = () => {
if (typeof window !== 'undefined') {
- // 在开发模式下,始终使用后端的WebSocket地址
- if (window.location.hostname === 'localhost' && (window.location.port === '3000' || window.location.port === '3001')) {
+ // 检查是否是 Next.js 开发服务器(端口 3000 或 3001)
+ const isNextDevServer = window.location.hostname === 'localhost' &&
+ (window.location.port === '3000' || window.location.port === '3001');
+
+ if (isNextDevServer) {
+ // 开发模式:通过 Next.js 开发服务器访问,连接到后端 WebSocket
return 'ws://localhost:8080/ws/p2p';
}
- // 在生产模式下,使用当前域名
+
+ // 生产模式或通过 Go 服务器访问:使用当前域名和端口
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
return `${protocol}//${window.location.host}/ws/p2p`;
}
diff --git a/chuan-next/src/lib/static-config.ts b/chuan-next/src/lib/static-config.ts
index 3e097bb..f953596 100644
--- a/chuan-next/src/lib/static-config.ts
+++ b/chuan-next/src/lib/static-config.ts
@@ -27,7 +27,9 @@ export const apiRoutes = [
// 客户端API配置(用于静态导出时的客户端请求)
export const clientApiConfig = {
- // 直接连接到 Go 后端
- baseUrl: 'http://localhost:8080', // 构建时可通过环境变量替换
- wsUrl: 'ws://localhost:8080/ws', // 构建时可通过环境变量替换
+ // 直接连接到 Go 后端 - 动态获取当前域名
+ baseUrl: typeof window !== 'undefined' ? window.location.origin : '',
+ wsUrl: typeof window !== 'undefined'
+ ? `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`
+ : '',
}
diff --git a/cmd/main.go b/cmd/main.go
index 1db3c7e..1ee4d09 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -2,6 +2,8 @@ package main
import (
"context"
+ "flag"
+ "fmt"
"log"
"net/http"
"os"
@@ -19,6 +21,19 @@ import (
)
func main() {
+ // 定义命令行参数
+ var port = flag.Int("port", 8080, "服务器监听端口")
+ var help = flag.Bool("help", false, "显示帮助信息")
+ flag.Parse()
+
+ // 显示帮助信息
+ if *help {
+ fmt.Println("文件传输服务器")
+ fmt.Println("用法:")
+ flag.PrintDefaults()
+ os.Exit(0)
+ }
+
// 初始化服务
p2pService := services.NewP2PService()
@@ -62,9 +77,15 @@ func main() {
r.Post("/api/create-room", h.CreateRoomHandler)
r.Get("/api/room-info", h.RoomInfoHandler)
+ // WebRTC API路由
+ r.Get("/api/webrtc-room-status", h.WebRTCRoomStatusHandler)
+
+ // 构建服务器地址
+ addr := fmt.Sprintf(":%d", *port)
+
// 启动服务器
srv := &http.Server{
- Addr: ":8080",
+ Addr: addr,
Handler: r,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
@@ -73,7 +94,7 @@ func main() {
// 优雅关闭
go func() {
- log.Printf("服务器启动在端口 :8080")
+ log.Printf("服务器启动在端口 %s", addr)
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("服务器启动失败: %v", err)
}
diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go
index 7ea1e20..32febf0 100644
--- a/internal/handlers/handlers.go
+++ b/internal/handlers/handlers.go
@@ -279,3 +279,52 @@ func (h *Handler) RoomInfoHandler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(response)
}
+
+// WebRTCRoomStatusHandler 获取WebRTC房间状态API
+func (h *Handler) WebRTCRoomStatusHandler(w http.ResponseWriter, r *http.Request) {
+ // 设置响应为JSON格式
+ w.Header().Set("Content-Type", "application/json")
+
+ if r.Method != http.MethodGet {
+ w.WriteHeader(http.StatusMethodNotAllowed)
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "success": false,
+ "message": "方法不允许",
+ })
+ return
+ }
+
+ code := r.URL.Query().Get("code")
+ if code == "" || len(code) != 6 {
+ w.WriteHeader(http.StatusBadRequest)
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "success": false,
+ "message": "请提供正确的6位房间码",
+ })
+ return
+ }
+
+ // 获取WebRTC房间状态
+ status := h.webrtcService.GetRoomStatus(code)
+
+ if !status["exists"].(bool) {
+ w.WriteHeader(http.StatusNotFound)
+ json.NewEncoder(w).Encode(map[string]interface{}{
+ "success": false,
+ "message": "房间不存在",
+ })
+ return
+ }
+
+ // 构建响应
+ response := map[string]interface{}{
+ "success": true,
+ "message": "房间状态获取成功",
+ "exists": status["exists"],
+ "sender_online": status["sender_online"],
+ "receiver_online": status["receiver_online"],
+ "created_at": status["created_at"],
+ }
+
+ json.NewEncoder(w).Encode(response)
+}
diff --git a/internal/services/webrtc_service.go b/internal/services/webrtc_service.go
index 6fbf8d1..e014310 100644
--- a/internal/services/webrtc_service.go
+++ b/internal/services/webrtc_service.go
@@ -53,6 +53,8 @@ type WebRTCMessage struct {
// HandleWebSocket 处理WebRTC信令WebSocket连接
func (ws *WebRTCService) HandleWebSocket(w http.ResponseWriter, r *http.Request) {
+ log.Printf("收到WebRTC WebSocket连接请求: %s", r.URL.String())
+
conn, err := ws.upgrader.Upgrade(w, r, nil)
if err != nil {
log.Printf("WebRTC WebSocket升级失败: %v", err)
@@ -64,6 +66,8 @@ func (ws *WebRTCService) HandleWebSocket(w http.ResponseWriter, r *http.Request)
code := r.URL.Query().Get("code")
role := r.URL.Query().Get("role")
+ log.Printf("WebRTC连接参数: code=%s, role=%s", code, role)
+
if code == "" || (role != "sender" && role != "receiver") {
log.Printf("WebRTC连接参数无效: code=%s, role=%s", code, role)
return
@@ -78,6 +82,8 @@ func (ws *WebRTCService) HandleWebSocket(w http.ResponseWriter, r *http.Request)
Room: code,
}
+ log.Printf("WebRTC客户端已创建: ID=%s, Role=%s, Room=%s", clientID, role, code)
+
// 添加客户端到房间
ws.addClientToRoom(code, client)
log.Printf("WebRTC %s连接到房间: %s (客户端ID: %s)", role, code, clientID)
@@ -86,6 +92,9 @@ func (ws *WebRTCService) HandleWebSocket(w http.ResponseWriter, r *http.Request)
defer func() {
ws.removeClientFromRoom(code, clientID)
log.Printf("WebRTC客户端断开连接: %s (房间: %s)", clientID, code)
+
+ // 通知房间内其他客户端对方已断开连接
+ ws.notifyRoomDisconnection(code, clientID, client.Role)
}()
// 处理消息
@@ -201,7 +210,45 @@ func (ws *WebRTCService) generateClientID() string {
return fmt.Sprintf("webrtc_client_%d", rand.Int63())
}
-// 获取房间状态
+// 通知房间内客户端有人断开连接
+func (ws *WebRTCService) notifyRoomDisconnection(roomCode string, disconnectedClientID string, disconnectedRole string) {
+ ws.roomsMux.Lock()
+ defer ws.roomsMux.Unlock()
+
+ room := ws.rooms[roomCode]
+ if room == nil {
+ return
+ }
+
+ // 构建断开连接通知消息
+ disconnectionMsg := &WebRTCMessage{
+ Type: "disconnection",
+ From: disconnectedClientID,
+ Payload: map[string]interface{}{
+ "role": disconnectedRole,
+ "message": "对方已停止传输",
+ },
+ }
+
+ // 通知房间内其他客户端
+ if room.Sender != nil && room.Sender.ID != disconnectedClientID {
+ err := room.Sender.Connection.WriteJSON(disconnectionMsg)
+ if err != nil {
+ log.Printf("通知发送方断开连接失败: %v", err)
+ } else {
+ log.Printf("已通知发送方: 对方已断开连接")
+ }
+ }
+
+ if room.Receiver != nil && room.Receiver.ID != disconnectedClientID {
+ err := room.Receiver.Connection.WriteJSON(disconnectionMsg)
+ if err != nil {
+ log.Printf("通知接收方断开连接失败: %v", err)
+ } else {
+ log.Printf("已通知接收方: 对方已断开连接")
+ }
+ }
+}
func (ws *WebRTCService) GetRoomStatus(code string) map[string]interface{} {
ws.roomsMux.RLock()
defer ws.roomsMux.RUnlock()
diff --git a/internal/web/frontend/404.html b/internal/web/frontend/404.html
index 8c92a02..ed949a2 100644
--- a/internal/web/frontend/404.html
+++ b/internal/web/frontend/404.html
@@ -1 +1 @@
-404: This page could not be found. 文件快传
404
This page could not be found.
\ No newline at end of file
+404: This page could not be found. 文件快传
404
This page could not be found.
\ No newline at end of file
diff --git a/internal/web/frontend/404/index.html b/internal/web/frontend/404/index.html
index 8c92a02..ed949a2 100644
--- a/internal/web/frontend/404/index.html
+++ b/internal/web/frontend/404/index.html
@@ -1 +1 @@
-404: This page could not be found. 文件快传
404
This page could not be found.
\ No newline at end of file
+404: This page could not be found. 文件快传
404
This page could not be found.
\ No newline at end of file
diff --git a/internal/web/frontend/_next/static/8bbWAyBmnmNj0Jk5ryXl4/_buildManifest.js b/internal/web/frontend/_next/static/8y8TXPJV_4IIplGqW3zUm/_buildManifest.js
similarity index 100%
rename from internal/web/frontend/_next/static/8bbWAyBmnmNj0Jk5ryXl4/_buildManifest.js
rename to internal/web/frontend/_next/static/8y8TXPJV_4IIplGqW3zUm/_buildManifest.js
diff --git a/internal/web/frontend/_next/static/8bbWAyBmnmNj0Jk5ryXl4/_ssgManifest.js b/internal/web/frontend/_next/static/8y8TXPJV_4IIplGqW3zUm/_ssgManifest.js
similarity index 100%
rename from internal/web/frontend/_next/static/8bbWAyBmnmNj0Jk5ryXl4/_ssgManifest.js
rename to internal/web/frontend/_next/static/8y8TXPJV_4IIplGqW3zUm/_ssgManifest.js
diff --git a/internal/web/frontend/_next/static/chunks/app/page-6f704b6eb3095813.js b/internal/web/frontend/_next/static/chunks/app/page-6f704b6eb3095813.js
deleted file mode 100644
index 02a87ee..0000000
--- a/internal/web/frontend/_next/static/chunks/app/page-6f704b6eb3095813.js
+++ /dev/null
@@ -1 +0,0 @@
-(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[974],{409:(e,s,t)=>{Promise.resolve().then(t.bind(t,1287))},1287:(e,s,t)=>{"use strict";t.d(s,{default:()=>_});var a=t(5155),l=t(2115),r=t(3559),n=t(2596),i=t(9688);function c(){for(var e=arguments.length,s=Array(e),t=0;t{let{className:t,...l}=e;return(0,a.jsx)(r.B8,{ref:s,className:c("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",t),...l})});d.displayName=r.B8.displayName;let m=l.forwardRef((e,s)=>{let{className:t,...l}=e;return(0,a.jsx)(r.l9,{ref:s,className:c("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",t),...l})});m.displayName=r.l9.displayName;let x=l.forwardRef((e,s)=>{let{className:t,...l}=e;return(0,a.jsx)(r.UC,{ref:s,className:c("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",t),...l})});x.displayName=r.UC.displayName;var h=t(9869),u=t(1497),g=t(4738);function p(){return(0,a.jsxs)("div",{className:"text-center mb-6 animate-fade-in-up",children:[(0,a.jsx)("h1",{className:"text-2xl sm:text-3xl md:text-4xl font-bold bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-600 bg-clip-text text-transparent mb-2",children:"文件快传"}),(0,a.jsxs)("p",{className:"text-sm sm:text-base text-slate-600 max-w-xl mx-auto leading-relaxed px-4 mb-3",children:["安全、快速、简单的传输服务",(0,a.jsx)("br",{}),(0,a.jsx)("span",{className:"text-xs sm:text-sm text-slate-500",children:"支持文件、文字、桌面共享 - 无需注册,即传即用"})]}),(0,a.jsx)("div",{className:"w-64 sm:w-80 md:w-96 lg:w-[32rem] xl:w-[40rem] h-0.5 bg-gradient-to-r from-blue-400 via-purple-400 to-indigo-400 mx-auto mt-4 mb-2 opacity-60"})]})}var f=t(5695),b=t(9708);let j=(0,t(2085).F)("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),v=l.forwardRef((e,s)=>{let{className:t,variant:l,size:r,asChild:n=!1,...i}=e,o=n?b.DX:"button";return(0,a.jsx)(o,{className:c(j({variant:l,size:r,className:t})),ref:s,...i})});v.displayName="Button";let N=l.forwardRef((e,s)=>{let{className:t,type:l,...r}=e;return(0,a.jsx)("input",{type:l,className:c("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",t),ref:s,...r})});N.displayName="Input";var w=t(1788),y=t(2486),k=t(4357),C=t(8164),S=t(7213),F=t(2657),R=t(6801);function T(e){let{onSendText:s,onReceiveText:t,websocket:r,isConnected:n=!1,currentRole:i,pickupCode:c,onCreateWebSocket:o}=e,d=(0,f.useSearchParams)(),m=(0,f.useRouter)(),[x,h]=(0,l.useState)("send"),[g,p]=(0,l.useState)(""),[b,j]=(0,l.useState)(""),[T,A]=(0,l.useState)(""),[D,W]=(0,l.useState)(!1),[E,L]=(0,l.useState)(!1),[z,P]=(0,l.useState)(0),[O,M]=(0,l.useState)([]),[I,B]=(0,l.useState)([]),[J,U]=(0,l.useState)(null),[_,q]=(0,l.useState)(null),[G,V]=(0,l.useState)(!1),{showToast:H}=(0,R.d)(),K=(0,l.useRef)(null),Q=(0,l.useRef)(null),X=(0,l.useRef)(null);(0,l.useEffect)(()=>{let e=d.get("mode");if("text"===d.get("type")&&e&&["send","receive"].includes(e)){h(e);let s=d.get("code");"receive"===e&&s&&6===s.length&&j(s.toUpperCase())}},[d]),(0,l.useEffect)(()=>{let e=e=>{var s,t,a,l,r,n;let c=e.detail;switch(console.log("TextTransfer收到WebSocket消息:",c),c.type){case"text-content":(null==(s=c.payload)?void 0:s.text)!==void 0&&(A(c.payload.text),"receiver"===i&&(p(c.payload.text),!G&&D&&(V(!0),H("成功加入文字房间!","success"))),X.current&&(clearTimeout(X.current),X.current=null),D&&W(!1));break;case"text-update":(null==(t=c.payload)?void 0:t.text)!==void 0&&(A(c.payload.text),"receiver"===i&&p(c.payload.text));break;case"text-send":(null==(a=c.payload)?void 0:a.text)&&(A(c.payload.text),H("收到新的文字内容!","success"));break;case"image-send":(null==(l=c.payload)?void 0:l.imageData)&&(console.log("接收到图片数据:",c.payload.imageData.substring(0,100)+"..."),c.payload.imageData.startsWith("data:image/")?(B(e=>[...e,c.payload.imageData]),H("收到新的图片!","success")):(console.error("无效的图片数据格式:",c.payload.imageData.substring(0,50)),H("收到的图片格式不正确","error")));break;case"room-status":(null==(r=c.payload)?void 0:r.sender_count)!==void 0&&(null==(n=c.payload)?void 0:n.receiver_count)!==void 0&&P(c.payload.sender_count+c.payload.receiver_count)}},s=e=>{let{code:s,reason:t}=e.detail;console.log("WebSocket连接关闭:",s,t),D&&(W(!1),1e3!==s&&H("取件码不存在或已过期","error"))},t=e=>{console.error("WebSocket连接错误:",e.detail),D&&(W(!1),H("取件码不存在或已过期","error"))};return window.addEventListener("websocket-message",e),window.addEventListener("websocket-close",s),window.addEventListener("websocket-error",t),()=>{window.removeEventListener("websocket-message",e),window.removeEventListener("websocket-close",s),window.removeEventListener("websocket-error",t),X.current&&clearTimeout(X.current)}},[i,H,G,D]);let Z=(0,l.useCallback)(e=>{h(e);let s=new URLSearchParams(d.toString());s.set("type","text"),s.set("mode",e),m.push("?".concat(s.toString()),{scroll:!1})},[d,m]),$=(0,l.useCallback)(e=>{r&&n&&(Q.current&&clearTimeout(Q.current),Q.current=setTimeout(()=>{r.send(JSON.stringify({type:"text-update",payload:{text:e}}))},300))},[r,n]),Y=(0,l.useCallback)(e=>{let s=e.target.value;p(s),n&&r&&$(s)},[n,r,$]),ee=(0,l.useCallback)(async()=>{if(!g.trim())return void H("请输入要传输的文字内容","error");W(!0);try{if(s){let e=await s(g);e&&(j(e),L(!0),H("房间创建成功!","success"),o&&o(e,"sender"))}}catch(e){console.error("创建房间失败:",e)}finally{W(!1)}},[g,s,o,H]),es=(0,l.useCallback)(async()=>{if(!b.trim()||6!==b.length)return void H("请输入正确的6位房间码","error");if(!D){W(!0),V(!1);try{let e=await fetch("/api/room-info?code=".concat(b)),s=await e.json();if(!e.ok||!s.success){H(s.message||"房间不存在或已过期","error"),W(!1);return}o&&(console.log("房间验证成功,手动加入房间:",b),o(b,"receiver"),X.current=setTimeout(()=>{D&&(W(!1),H("取件码不存在或已过期","error"))},8e3))}catch(e){console.error("加入房间失败:",e),H("网络错误,请稍后重试","error"),W(!1)}}},[b,o,H,D]),et=(0,l.useCallback)(()=>{r&&n&&g.trim()&&(r.send(JSON.stringify({type:"text-send",payload:{text:g}})),H("文字已发送!","success"))},[r,n,g,H]),ea=(0,l.useCallback)(e=>new Promise((s,t)=>{let a=document.createElement("canvas"),l=a.getContext("2d"),r=document.createElement("img");if(!l)return void t(Error("无法创建Canvas上下文"));r.onload=()=>{try{let{width:e,height:t}=r;e>t?e>800&&(t=800*t/e,e=800):t>600&&(e=600*e/t,t=600),a.width=e,a.height=t,l.fillStyle="#FFFFFF",l.fillRect(0,0,e,t),l.drawImage(r,0,0,e,t);let n=a.toDataURL("image/jpeg",.8);console.log("图片压缩完成,数据长度:",n.length,"前100字符:",n.substring(0,100)),s(n)}catch(e){t(Error("图片压缩失败: "+e))}},r.onerror=()=>t(Error("图片加载失败"));let n=new FileReader;n.onload=e=>{var s;(null==(s=e.target)?void 0:s.result)?r.src=e.target.result:t(Error("文件读取失败"))},n.onerror=()=>t(Error("文件读取失败")),n.readAsDataURL(e)}),[]),el=(0,l.useCallback)(async e=>{var s;let t=null==(s=e.clipboardData)?void 0:s.items;if(t)for(let e=0;e[...e,s]),r&&n&&(r.send(JSON.stringify({type:"image-send",payload:{imageData:s}})),H("图片已发送!","success"))}catch(e){console.error("图片处理失败:",e),H("图片处理失败,请重试","error")}}}},[r,n,H,ea]),er=(0,l.useCallback)(async e=>{try{await navigator.clipboard.writeText(e),H("已复制到剪贴板!","success")}catch(e){H("复制失败","error")}},[H]),en=(0,l.useCallback)(async e=>{let s=window.location.origin+window.location.pathname,t="".concat(s,"?type=text&mode=receive&code=").concat(e);await er(t)},[er]),ei=(0,l.useCallback)((e,s)=>{let t=document.createElement("a");t.download="image_".concat(s+1,".jpg"),t.href=e,document.body.appendChild(t),t.click(),document.body.removeChild(t),H("图片已下载!","success")},[H]);return(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsx)("div",{className:"flex justify-center mb-6",children:(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg",children:[(0,a.jsxs)(v,{variant:"send"===x?"default":"ghost",onClick:()=>Z("send"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(y.A,{className:"w-4 h-4 mr-2"}),"发送文字"]}),(0,a.jsxs)(v,{variant:"receive"===x?"default":"ghost",onClick:()=>Z("receive"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(w.A,{className:"w-4 h-4 mr-2"}),"加入房间"]})]})}),"send"===x?(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(u.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"传送文字"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:E?"实时编辑,对方可以同步看到":"输入要传输的文本内容"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:E?n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-red-500"}),(0,a.jsx)("span",{className:"text-red-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})]}),z>0&&(0,a.jsxs)("div",{className:"mt-1 text-xs text-blue-600",children:[z," 人在线"]})]})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)("textarea",{ref:K,value:g,onChange:Y,onPaste:el,placeholder:"在这里输入要传输的文本内容... \uD83D\uDCA1 提示:支持实时同步编辑,可以直接粘贴图片 (Ctrl+V)",className:"w-full min-h-[150px] p-4 border-2 border-slate-200 rounded-xl focus:border-blue-500 focus:ring-blue-500 bg-white/80 backdrop-blur-sm resize-none",disabled:D}),E&&n&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-emerald-100 text-emerald-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-emerald-500 rounded-full animate-pulse"}),(0,a.jsx)("span",{children:"实时同步"})]})}),E&&!n&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-orange-100 text-orange-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-orange-500 rounded-full"}),(0,a.jsx)("span",{children:"连接中..."})]})})]}),(0,a.jsxs)("div",{className:"flex justify-between text-sm text-slate-500",children:[(0,a.jsxs)("span",{children:[g.length," 字符"]}),(0,a.jsx)("span",{children:"最大 50,000 字符"})]}),E?(0,a.jsx)("div",{className:"space-y-4",children:(0,a.jsx)("div",{className:"p-4 bg-gradient-to-r from-emerald-50 to-teal-50 rounded-xl border border-emerald-200",children:(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)("p",{className:"text-sm text-emerald-700 mb-2",children:"房间码"}),(0,a.jsx)("div",{className:"text-2xl font-bold font-mono text-emerald-600 mb-3",children:b}),(0,a.jsxs)("div",{className:"flex flex-wrap gap-2 justify-center",children:[(0,a.jsxs)(v,{onClick:()=>er(b),size:"sm",className:"bg-emerald-500 hover:bg-emerald-600 text-white",children:[(0,a.jsx)(k.A,{className:"w-4 h-4 mr-2"}),"复制房间码"]}),(0,a.jsxs)(v,{onClick:()=>en(b),size:"sm",className:"bg-purple-500 hover:bg-purple-600 text-white",children:[(0,a.jsx)(C.A,{className:"w-4 h-4 mr-2"}),"复制链接"]}),(0,a.jsxs)(v,{onClick:et,size:"sm",className:"bg-blue-500 hover:bg-blue-600 text-white",disabled:!g.trim(),children:[(0,a.jsx)(y.A,{className:"w-4 h-4 mr-2"}),"发送文字"]})]})]})})}):(0,a.jsx)(v,{onClick:ee,disabled:!g.trim()||g.length>5e4||D,className:"w-full h-12 bg-gradient-to-r from-blue-500 to-indigo-500 hover:from-blue-600 hover:to-indigo-600 text-white text-lg font-medium rounded-xl shadow-lg",children:D?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"创建房间..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(y.A,{className:"w-5 h-5 mr-2"}),"创建文字传输房间"]})}),"send"===x&&O.length>0&&(0,a.jsxs)("div",{className:"mt-6",children:[(0,a.jsxs)("h3",{className:"text-lg font-medium text-slate-800 mb-3 flex items-center",children:[(0,a.jsx)(S.A,{className:"w-5 h-5 mr-2"}),"已发送的图片 (",O.length,")"]}),(0,a.jsx)("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-3",children:O.map((e,s)=>(0,a.jsxs)("div",{className:"relative group overflow-hidden",children:[(0,a.jsx)("img",{src:e,alt:"图片 ".concat(s+1),className:"w-full h-24 object-cover rounded-lg border-2 border-slate-200 hover:border-blue-400 transition-all duration-200 cursor-pointer bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",onClick:()=>q(e),onError:s=>{console.error("图片加载失败:",e),s.currentTarget.style.display="none"}}),(0,a.jsx)("div",{className:"absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-opacity rounded-lg"}),(0,a.jsxs)("div",{className:"absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1",children:[(0,a.jsx)("button",{onClick:s=>{s.stopPropagation(),q(e)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"预览图片",children:(0,a.jsx)(F.A,{className:"w-3.5 h-3.5 text-slate-600"})}),(0,a.jsx)("button",{onClick:t=>{t.stopPropagation(),ei(e,s)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"下载图片",children:(0,a.jsx)(w.A,{className:"w-3.5 h-3.5 text-slate-600"})})]}),(0,a.jsx)("div",{className:"absolute bottom-1 left-1 bg-black bg-opacity-50 text-white text-xs px-1.5 py-0.5 rounded",children:s+1})]},s))})]})]})]}):(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"加入房间"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:T||g?n?"已连接,可以实时查看和编辑":"连接断开,等待重连":"输入6位房间码来获取文字内容"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:T||g?n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-red-500"}),(0,a.jsx)("span",{className:"text-red-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})]}),z>0&&(0,a.jsxs)("div",{className:"mt-1 text-xs text-blue-600",children:[z," 人在线"]})]})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)(N,{value:b,onChange:e=>j(e.target.value.toUpperCase().slice(0,6)),placeholder:"请输入房间码",className:"text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-emerald-500 focus:ring-emerald-500 bg-white/80 backdrop-blur-sm",maxLength:6,disabled:D}),(0,a.jsx)(v,{onClick:es,disabled:6!==b.length||D,className:"w-full h-12 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white text-lg font-medium rounded-xl shadow-lg",children:D?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"连接中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(w.A,{className:"w-5 h-5 mr-2"}),"加入房间"]})}),(T||g)&&(0,a.jsxs)("div",{className:"mt-6 space-y-4",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)("textarea",{value:T||g,readOnly:"receiver"!==i,onChange:"receiver"===i?Y:void 0,className:"w-full min-h-[150px] p-4 border-2 border-emerald-200 rounded-xl bg-emerald-50/50 backdrop-blur-sm resize-none"}),"receiver"===i&&n&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-emerald-100 text-emerald-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-emerald-500 rounded-full animate-pulse"}),(0,a.jsx)("span",{children:"实时同步"})]})}),"receiver"===i&&!n&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-orange-100 text-orange-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-orange-500 rounded-full"}),(0,a.jsx)("span",{children:"连接中..."})]})})]}),(0,a.jsxs)(v,{onClick:()=>er(T||g),className:"w-full h-12 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white text-lg font-medium rounded-xl shadow-lg",children:[(0,a.jsx)(k.A,{className:"w-5 h-5 mr-2"}),"复制文字"]})]}),"receive"===x&&I.length>0&&(0,a.jsxs)("div",{className:"mt-6",children:[(0,a.jsxs)("h3",{className:"text-lg font-medium text-slate-800 mb-3 flex items-center",children:[(0,a.jsx)(S.A,{className:"w-5 h-5 mr-2"}),"接收到的图片 (",I.length,")"]}),(0,a.jsx)("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-3",children:I.map((e,s)=>(0,a.jsxs)("div",{className:"relative group overflow-hidden",children:[(0,a.jsx)("img",{src:e,alt:"图片 ".concat(s+1),className:"w-full h-24 object-cover rounded-lg border-2 border-slate-200 hover:border-emerald-400 transition-all duration-200 cursor-pointer bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",onClick:()=>q(e),onLoad:e=>{console.log("图片 ".concat(s+1," 加载成功"))},onError:t=>{console.error("图片 ".concat(s+1," 加载失败:"),e.substring(0,100)),t.currentTarget.style.backgroundColor="#f1f5f9",t.currentTarget.style.display="flex",t.currentTarget.style.alignItems="center",t.currentTarget.style.justifyContent="center",t.currentTarget.innerHTML='图片加载失败 '}}),(0,a.jsx)("div",{className:"absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-opacity rounded-lg"}),(0,a.jsxs)("div",{className:"absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1",children:[(0,a.jsx)("button",{onClick:s=>{s.stopPropagation(),q(e)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"预览图片",children:(0,a.jsx)(F.A,{className:"w-3.5 h-3.5 text-slate-600"})}),(0,a.jsx)("button",{onClick:t=>{t.stopPropagation(),ei(e,s)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"下载图片",children:(0,a.jsx)(w.A,{className:"w-3.5 h-3.5 text-slate-600"})})]}),(0,a.jsx)("div",{className:"absolute bottom-1 left-1 bg-black bg-opacity-50 text-white text-xs px-1.5 py-0.5 rounded",children:s+1})]},s))})]})]})]}),_&&(0,a.jsx)(e=>{let{src:s,onClose:t}=e;return(0,a.jsx)("div",{className:"fixed inset-0 bg-slate-900/80 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fade-in",onClick:t,children:(0,a.jsx)("div",{className:"relative max-w-[90vw] max-h-[90vh] animate-scale-in",children:(0,a.jsxs)("div",{className:"relative bg-white rounded-2xl overflow-hidden shadow-2xl",children:[(0,a.jsx)("img",{src:s,alt:"预览",className:"max-w-full max-h-[80vh] object-contain block bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",onClick:e=>e.stopPropagation(),onError:e=>{console.error("预览图片加载失败:",s)}}),(0,a.jsx)("div",{className:"absolute top-0 left-0 right-0 bg-gradient-to-b from-slate-900/60 to-transparent p-4",children:(0,a.jsxs)("div",{className:"flex justify-between items-center",children:[(0,a.jsx)("h3",{className:"text-white font-medium text-lg",children:"图片预览"}),(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)("button",{onClick:e=>{e.stopPropagation();let t=O.indexOf(s);-1===t&&(t=I.indexOf(s)),ei(s,t>=0?t:0)},className:"bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white p-2 rounded-lg shadow-lg transition-all hover:scale-105",title:"下载图片",children:(0,a.jsx)(w.A,{className:"w-5 h-5"})}),(0,a.jsx)("button",{onClick:t,className:"bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white p-2 rounded-lg shadow-lg transition-all hover:scale-105",title:"关闭预览",children:(0,a.jsx)("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,a.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})]})}),(0,a.jsx)("div",{className:"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-slate-900/60 to-transparent p-4",children:(0,a.jsx)("div",{className:"text-white text-sm opacity-80",children:"点击空白区域关闭预览"})})]})})})},{src:_,onClose:()=>q(null)})]})}var A=t(6683),D=t(5690),W=t(8979);function E(e){let{onStartSharing:s,onStopSharing:t,onJoinSharing:r}=e,n=(0,f.useSearchParams)(),i=(0,f.useRouter)(),[c,o]=(0,l.useState)("share"),[d,m]=(0,l.useState)(""),[x,h]=(0,l.useState)(""),[u,p]=(0,l.useState)(!1),[b,j]=(0,l.useState)(!1),[w,y]=(0,l.useState)(!1),{showToast:C}=(0,R.d)();(0,l.useEffect)(()=>{let e=n.get("mode");"desktop"===n.get("type")&&e&&("send"===e?o("share"):"receive"===e&&o("view"))},[n]);let S=(0,l.useCallback)(e=>{o(e);let s=new URLSearchParams(n.toString());s.set("type","desktop"),s.set("mode","share"===e?"send":"receive"),i.push("?".concat(s.toString()),{scroll:!1})},[n,i]),F=(0,l.useCallback)(async()=>{if(s){y(!0);try{let e=await s();m(e),p(!0),C("桌面共享已开始!","success")}catch(e){console.error("开始共享失败:",e),C("开始共享失败,请重试","error")}finally{y(!1)}}},[s,C]),T=(0,l.useCallback)(async()=>{if(t){y(!0);try{await t(),p(!1),m(""),C("桌面共享已停止","success")}catch(e){console.error("停止共享失败:",e),C("停止共享失败","error")}finally{y(!1)}}},[t,C]),E=(0,l.useCallback)(async()=>{if(x.trim()&&r){y(!0);try{await r(x),j(!0),C("已连接到桌面共享!","success")}catch(e){console.error("连接失败:",e),C("连接失败,请检查连接码","error")}finally{y(!1)}}},[x,r,C]),L=(0,l.useCallback)(async e=>{try{await navigator.clipboard.writeText(e),C("已复制到剪贴板!","success")}catch(e){C("复制失败","error")}},[C]);return(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsx)("div",{className:"flex justify-center mb-6",children:(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg",children:[(0,a.jsxs)(v,{variant:"share"===c?"default":"ghost",onClick:()=>S("share"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(A.A,{className:"w-4 h-4 mr-2"}),"共享桌面"]}),(0,a.jsxs)(v,{variant:"view"===c?"default":"ghost",onClick:()=>S("view"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(g.A,{className:"w-4 h-4 mr-2"}),"观看桌面"]})]})}),"share"===c?(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(A.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"共享桌面"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:u?"桌面共享进行中":"开始共享您的桌面屏幕"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:u?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]}),u&&d&&(0,a.jsx)("div",{className:"mt-1 text-xs text-purple-600",children:d})]})]}),(0,a.jsx)("div",{className:"space-y-4",children:u?(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)("div",{className:"p-4 bg-gradient-to-r from-purple-50 to-pink-50 rounded-xl border border-purple-200",children:(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)("p",{className:"text-sm text-purple-700 mb-2",children:"连接码"}),(0,a.jsx)("div",{className:"text-2xl font-bold font-mono text-purple-600 mb-3",children:d}),(0,a.jsxs)(v,{onClick:()=>L(d),size:"sm",className:"bg-purple-500 hover:bg-purple-600 text-white",children:[(0,a.jsx)(k.A,{className:"w-4 h-4 mr-2"}),"复制连接码"]})]})}),(0,a.jsx)(v,{onClick:T,disabled:w,className:"w-full h-12 bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white text-lg font-medium rounded-xl shadow-lg",children:w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"停止中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(W.A,{className:"w-5 h-5 mr-2"}),"停止共享"]})})]}):(0,a.jsx)(v,{onClick:F,disabled:w,className:"w-full h-12 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white text-lg font-medium rounded-xl shadow-lg",children:w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"启动中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(D.A,{className:"w-5 h-5 mr-2"}),"开始共享桌面"]})})})]}):(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(g.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"观看桌面"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:b?"正在观看桌面共享":"输入连接码观看他人的桌面"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:b?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]}),b&&(0,a.jsx)("div",{className:"mt-1 text-xs text-indigo-600",children:"观看中"})]})]}),(0,a.jsx)("div",{className:"space-y-4",children:b?(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)("div",{className:"aspect-video bg-slate-900 rounded-xl flex items-center justify-center text-white",children:(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)(g.A,{className:"w-12 h-12 mx-auto mb-2 opacity-50"}),(0,a.jsx)("p",{className:"text-sm opacity-75",children:"桌面共享画面"})]})}),(0,a.jsxs)(v,{onClick:()=>j(!1),className:"w-full h-12 bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white text-lg font-medium rounded-xl shadow-lg",children:[(0,a.jsx)(W.A,{className:"w-5 h-5 mr-2"}),"断开连接"]})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(N,{value:x,onChange:e=>h(e.target.value.toUpperCase().slice(0,6)),placeholder:"请输入连接码",className:"text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-indigo-500 focus:ring-indigo-500 bg-white/80 backdrop-blur-sm",maxLength:6,disabled:w}),(0,a.jsx)(v,{onClick:E,disabled:6!==x.length||w,className:"w-full h-12 bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600 text-white text-lg font-medium rounded-xl shadow-lg",children:w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"连接中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(g.A,{className:"w-5 h-5 mr-2"}),"连接桌面"]})})]})})]})]})}var L=t(9803),z=t(227),P=t(9022),O=t(7434),M=t(4416);function I(e){let{selectedFiles:s,fileList:t=[],onFilesChange:r,onGenerateCode:n,pickupCode:i,pickupLink:c,onCopyCode:o,onCopyLink:d,onAddMoreFiles:m,onRemoveFile:x,onClearFiles:u,onReset:g,disabled:p=!1,isConnected:f=!1,isWebSocketConnected:b=!1}=e,[j,N]=(0,l.useState)(!1),w=(0,l.useRef)(null),y=(0,l.useCallback)(e=>{e.preventDefault(),N(!0)},[]),k=(0,l.useCallback)(e=>{e.preventDefault(),e.currentTarget.contains(e.relatedTarget)||N(!1)},[]),C=(0,l.useCallback)(e=>{e.preventDefault(),N(!1);let t=Array.from(e.dataTransfer.files);t.length>0&&r([...s,...t])},[s,r]),F=(0,l.useCallback)(e=>{let t=Array.from(e.target.files||[]);t.length>0&&r([...s,...t])},[s,r]),R=(0,l.useCallback)(e=>{let t=s.filter((s,t)=>t!==e);r(t),x&&x(t)},[s,r,x]),T=(0,l.useCallback)(()=>{w.current&&w.current.click()},[]);return 0!==s.length||i?(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center mb-4 sm:mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(O.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"text-lg font-semibold text-slate-800",children:"已选择文件"}),(0,a.jsxs)("p",{className:"text-sm text-slate-500",children:[s.length," 个文件准备传输"]})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:b?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:f?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):i?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]})]})]}),(0,a.jsx)("div",{className:"space-y-3 mb-4 sm:mb-6",children:s.map((e,s)=>{var l;let r=t.find(s=>s.name===e.name&&s.size===e.size);null==r||r.status;let n=(null==r?void 0:r.progress)||0,i=(null==r?void 0:r.status)||"ready";return(0,a.jsxs)("div",{className:"group bg-gradient-to-r from-slate-50 to-blue-50 border border-slate-200 rounded-xl hover:shadow-md transition-all duration-200",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between p-3 sm:p-4",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 sm:space-x-4 min-w-0 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-lg flex items-center justify-center flex-shrink-0",children:(l=e.type).startsWith("image/")?(0,a.jsx)(S.A,{className:"w-5 h-5 text-white"}):l.startsWith("video/")?(0,a.jsx)(L.A,{className:"w-5 h-5 text-white"}):l.startsWith("audio/")?(0,a.jsx)(z.A,{className:"w-5 h-5 text-white"}):l.includes("zip")||l.includes("rar")?(0,a.jsx)(P.A,{className:"w-5 h-5 text-white"}):(0,a.jsx)(O.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,a.jsx)("p",{className:"font-medium text-slate-800 truncate text-sm sm:text-base",children:e.name}),(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,a.jsx)("p",{className:"text-xs sm:text-sm text-slate-500",children:(e=>{if(0===e)return"0 Bytes";let s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+" "+["Bytes","KB","MB","GB"][s]})(e.size)}),"downloading"===i&&(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-1 h-1 bg-orange-500 rounded-full animate-pulse"}),(0,a.jsx)("span",{className:"text-xs text-orange-600 font-medium",children:"传输中"})]}),"completed"===i&&(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-1 h-1 bg-emerald-500 rounded-full"}),(0,a.jsx)("span",{className:"text-xs text-emerald-600 font-medium",children:"已完成"})]})]})]})]}),(0,a.jsx)(v,{variant:"ghost",size:"sm",onClick:()=>R(s),disabled:p||"downloading"===i,className:"opacity-0 group-hover:opacity-100 text-slate-400 hover:text-red-500 hover:bg-red-50 transition-all duration-200 flex-shrink-0 ml-2 disabled:opacity-50",children:(0,a.jsx)(M.A,{className:"w-4 h-4"})})]}),("downloading"===i||"completed"===i)&&n>0&&(0,a.jsx)("div",{className:"px-3 sm:px-4 pb-3 sm:pb-4",children:(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsxs)("div",{className:"flex justify-between text-xs text-slate-600",children:[(0,a.jsx)("span",{children:"downloading"===i?"正在发送...":"发送完成"}),(0,a.jsxs)("span",{className:"font-medium",children:[n.toFixed(1),"%"]})]}),(0,a.jsx)("div",{className:"w-full bg-slate-200 rounded-full h-2",children:(0,a.jsx)("div",{className:"h-2 rounded-full transition-all duration-300 ".concat("completed"===i?"bg-gradient-to-r from-emerald-500 to-emerald-600":"bg-gradient-to-r from-orange-500 to-orange-600"),style:{width:"".concat(n,"%")}})})]})})]},"".concat(e.name,"-").concat(e.size,"-").concat(s))})}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-3",children:[!i&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(v,{onClick:n,disabled:p||0===s.length,className:"button-primary text-white px-6 sm:px-8 py-3 rounded-xl font-medium flex-1 min-w-0 shadow-lg",children:[(0,a.jsx)(h.A,{className:"w-5 h-5 mr-2"}),"生成取件码"]}),(0,a.jsx)(v,{onClick:m,variant:"outline",disabled:p,className:"px-6 sm:px-8 py-3 rounded-xl font-medium",children:"添加文件"}),(0,a.jsx)(v,{onClick:g,variant:"outline",disabled:p,className:"text-red-600 hover:bg-red-50 px-6 sm:px-8 py-3 rounded-xl font-medium",children:"重新选择"})]}),i&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(v,{variant:"outline",onClick:m,disabled:p,className:"px-6 py-3 rounded-xl border-slate-300 text-slate-600 hover:bg-slate-50 flex-1",children:"添加更多文件"}),s.length>0&&u&&(0,a.jsx)(v,{variant:"outline",onClick:u,disabled:p,className:"px-6 py-3 rounded-xl border-orange-300 text-orange-600 hover:bg-orange-50",children:"清空文件"})]}),(0,a.jsx)(v,{variant:"outline",onClick:g,disabled:p,className:"px-6 py-3 rounded-xl border-red-300 text-red-600 hover:bg-red-50",children:"关闭房间"})]})]}),i&&(0,a.jsxs)("div",{className:"border-t border-slate-200 pt-6",children:[(0,a.jsxs)("div",{className:"text-center mb-4 sm:mb-6",children:[(0,a.jsx)("div",{className:"w-12 h-12 sm:w-16 sm:h-16 mx-auto mb-4 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-2xl flex items-center justify-center animate-float",children:(0,a.jsx)(O.A,{className:"w-6 h-6 sm:w-8 sm:h-8 text-white"})}),(0,a.jsx)("h3",{className:"text-xl sm:text-2xl font-bold bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent mb-2",children:"取件码生成成功!"}),(0,a.jsx)("p",{className:"text-sm sm:text-base text-slate-600",children:"分享以下信息给接收方"})]}),(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"block text-sm font-medium text-slate-700 mb-3",children:"取件码"}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-3",children:[(0,a.jsx)("div",{className:"flex-1 code-display rounded-xl p-4 sm:p-6 text-center",children:(0,a.jsx)("div",{className:"text-2xl sm:text-3xl font-bold font-mono bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent tracking-wider",children:i})}),(0,a.jsx)(v,{onClick:o,className:"px-4 sm:px-6 py-3 bg-emerald-500 hover:bg-emerald-600 text-white rounded-xl font-medium shadow-lg transition-all duration-200 hover:shadow-xl w-full sm:w-auto",children:"复制"})]})]}),c&&(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"block text-sm font-medium text-slate-700 mb-3",children:"取件链接"}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-3",children:[(0,a.jsx)("div",{className:"flex-1 code-display rounded-xl p-3 sm:p-4",children:(0,a.jsx)("div",{className:"text-xs sm:text-sm text-slate-700 break-all font-mono",children:c})}),(0,a.jsx)(v,{onClick:d,className:"px-4 sm:px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white rounded-xl font-medium shadow-lg transition-all duration-200 hover:shadow-xl w-full sm:w-auto",children:"复制"})]})]})]}),(0,a.jsx)("div",{className:"mt-4 sm:mt-6 p-3 sm:p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200",children:(0,a.jsxs)("p",{className:"text-xs sm:text-sm text-slate-600 text-center",children:["\uD83D\uDCA1 ",(0,a.jsx)("span",{className:"font-medium",children:"使用提示:"}),"接收方输入取件码或访问取件链接即可下载文件"]})})]})]}):(0,a.jsxs)("div",{className:"space-y-6",children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(h.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"选择文件"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:"拖拽文件到下方区域或点击选择文件"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})]})]})]}),(0,a.jsxs)("div",{className:"upload-area rounded-xl p-6 sm:p-8 md:p-12 text-center cursor-pointer ".concat(j?"drag-active":""),onDragOver:y,onDragLeave:k,onDrop:C,onClick:T,children:[(0,a.jsxs)("div",{className:"transition-all duration-300 ".concat(j?"scale-110":""),children:[(0,a.jsx)("div",{className:"w-16 h-16 sm:w-20 sm:h-20 mx-auto mb-4 sm:mb-6 bg-gradient-to-br from-blue-100 to-indigo-100 rounded-full flex items-center justify-center",children:(0,a.jsx)(h.A,{className:"w-8 h-8 sm:w-10 sm:h-10 transition-colors duration-300 ".concat(j?"text-blue-600":"text-slate-400")})}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)("p",{className:"text-lg sm:text-xl font-medium text-slate-700",children:j?"释放文件":"拖拽文件到这里"}),(0,a.jsxs)("p",{className:"text-sm sm:text-base text-slate-500",children:["或者 ",(0,a.jsx)("span",{className:"text-blue-600 font-medium underline",children:"点击选择文件"})]}),(0,a.jsx)("p",{className:"text-xs sm:text-sm text-slate-400 mt-4",children:"支持多个文件同时上传,WebRTC点对点传输"})]})]}),(0,a.jsx)("input",{ref:w,type:"file",multiple:!0,className:"hidden",onChange:F,disabled:p})]})]})}function B(e){let{onJoinRoom:s,files:t,onDownloadFile:r,isConnected:n,isConnecting:i,isWebSocketConnected:c=!1,downloadedFiles:o,error:d=null,onReset:m}=e,[x,h]=(0,l.useState)(""),[u,g]=(0,l.useState)(!1),{showToast:p}=(0,R.d)(),f=async e=>{try{g(!0),console.log("开始验证取件码:",e);let s=await fetch("/api/room-info?code=".concat(e)),t=await s.json();if(console.log("验证响应:",{status:s.status,data:t}),!s.ok||!t.success){let e=t.message||"取件码验证失败";return p(e,"error"),console.log("验证失败:",e),!1}return console.log("取件码验证成功:",t.room),!0}catch(e){return console.error("验证取件码时发生错误:",e),p("网络错误,请检查连接后重试","error"),!1}finally{g(!1)}},b=(0,l.useCallback)(async e=>{if(e.preventDefault(),6===x.length){let e=x.toUpperCase();await f(e)&&s(e)}},[x,s]),j=(0,l.useCallback)(e=>{let s=e.target.value.replace(/[^A-Z0-9]/g,"").toUpperCase();s.length<=6&&h(s)},[]);return(l.useEffect(()=>{if(d&&!i&&!n&&!u){let e=setTimeout(()=>{console.log("重置取件码输入"),h("")},3e3);return()=>clearTimeout(e)}},[d,i,n,u]),(n||i)&&0===t.length)?(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"等待文件"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:n?"已连接到房间,等待发送方选择文件...":"正在连接到房间..."})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:c?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]})})]})]})]}),(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)("div",{className:"flex items-center justify-center space-x-4 mb-6",children:(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)("div",{className:"w-3 h-3 rounded-full mr-2 ".concat(n?"bg-emerald-500 animate-pulse":"bg-orange-500 animate-spin")}),(0,a.jsx)("span",{className:"text-sm font-medium ".concat(n?"text-emerald-600":"text-orange-600"),children:n?"连接已建立":"连接中..."})]})}),(0,a.jsx)("div",{className:"flex justify-center space-x-1 mb-6",children:[void 0,void 0,void 0].map((e,s)=>(0,a.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full animate-bounce",style:{animationDelay:"".concat(.1*s,"s")}},s))}),(0,a.jsx)("div",{className:"p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200",children:(0,a.jsxs)("p",{className:"text-xs sm:text-sm text-slate-600 text-center",children:["\uD83D\uDCA1 ",(0,a.jsx)("span",{className:"font-medium",children:"提示:"}),"房间已连接,发送方清空文件列表后您会看到此界面,等待对方重新选择文件"]})})]})]}):t.length>0?(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"text-lg font-semibold text-slate-800",children:"可下载文件"}),(0,a.jsx)("p",{className:"text-sm text-slate-500",children:n?(0,a.jsx)("span",{className:"text-emerald-600",children:"✅ 已连接,可以下载文件"}):(0,a.jsx)("span",{className:"text-amber-600",children:"⏳ 正在建立连接..."})})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:c?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]})})]}),(0,a.jsxs)("div",{className:"mt-1 text-xs text-slate-400",children:[t.length," 个文件"]})]})]}),(0,a.jsx)("div",{children:(0,a.jsx)("div",{className:"space-y-3 sm:space-y-4",children:t.map(e=>{var s;let t="downloading"===e.status,l="completed"===e.status,i=null==o?void 0:o.has(e.id),c=e.progress;return console.log("文件状态:",{fileName:e.name,status:e.status,progress:e.progress,isDownloading:t,currentProgress:c}),(0,a.jsxs)("div",{className:"bg-gradient-to-r from-slate-50 to-blue-50 border border-slate-200 rounded-xl p-3 sm:p-4 hover:shadow-md transition-all duration-200",children:[(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row sm:items-center justify-between mb-3 gap-3",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 sm:space-x-4 flex-1 min-w-0",children:[(0,a.jsx)("div",{className:"w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-lg flex items-center justify-center flex-shrink-0",children:(s=e.type).startsWith("image/")?(0,a.jsx)(S.A,{className:"w-5 h-5 text-white"}):s.startsWith("video/")?(0,a.jsx)(L.A,{className:"w-5 h-5 text-white"}):s.startsWith("audio/")?(0,a.jsx)(z.A,{className:"w-5 h-5 text-white"}):s.includes("zip")||s.includes("rar")?(0,a.jsx)(P.A,{className:"w-5 h-5 text-white"}):(0,a.jsx)(O.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,a.jsx)("p",{className:"font-medium text-slate-800 truncate text-sm sm:text-base",children:e.name}),(0,a.jsx)("p",{className:"text-sm text-slate-500",children:(e=>{if(0===e)return"0 Bytes";let s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+" "+["Bytes","KB","MB","GB"][s]})(e.size)}),i&&(0,a.jsx)("p",{className:"text-xs text-emerald-600 font-medium",children:"✅ 传输完成,点击保存"}),t&&(0,a.jsxs)("p",{className:"text-xs text-blue-600 font-medium",children:["⏳ 传输中...",c.toFixed(1),"%"]})]})]}),(0,a.jsxs)(v,{onClick:()=>r(e.id),disabled:!n||t,className:"px-6 py-2 rounded-lg font-medium shadow-lg transition-all duration-200 hover:shadow-xl ".concat(i?"bg-gradient-to-r from-blue-500 to-indigo-500 hover:from-blue-600 hover:to-indigo-600 text-white":t?"bg-slate-300 text-slate-500 cursor-not-allowed":"bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white"),children:[(0,a.jsx)(w.A,{className:"w-4 h-4 mr-2"}),t?"传输中...":i?"保存文件":"开始传输"]})]}),(t||l)&&c>0&&(0,a.jsxs)("div",{className:"mt-3 space-y-2",children:[(0,a.jsxs)("div",{className:"flex justify-between text-sm text-slate-600",children:[(0,a.jsx)("span",{children:i?"传输完成":"正在传输..."}),(0,a.jsxs)("span",{className:"font-medium",children:[c.toFixed(1),"%"]})]}),(0,a.jsx)("div",{className:"w-full bg-slate-200 rounded-full h-2",children:(0,a.jsx)("div",{className:"h-2 rounded-full transition-all duration-300 ".concat(i?"bg-gradient-to-r from-emerald-500 to-emerald-600":"bg-gradient-to-r from-emerald-500 to-teal-500"),style:{width:"".concat(c,"%")}})})]})]},e.id)})})})]}):(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center mb-6 sm:mb-8",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"输入取件码"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:"请输入6位取件码来获取文件"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:i?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"WS"})]}):c?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):i?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]})]})]}),(0,a.jsxs)("form",{onSubmit:b,className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{className:"space-y-3",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)(N,{value:x,onChange:j,placeholder:"请输入取件码",className:"text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-emerald-500 focus:ring-emerald-500 bg-white/80 backdrop-blur-sm pb-2 sm:pb-4",maxLength:6,disabled:u||i}),(0,a.jsx)("div",{className:"absolute inset-x-0 -bottom-4 sm:-bottom-6 flex justify-center space-x-1 sm:space-x-2",children:[...Array(6)].map((e,s)=>(0,a.jsx)("div",{className:"w-1.5 h-1.5 sm:w-2 sm:h-2 rounded-full transition-all duration-200 ".concat(s{let e=(0,f.useSearchParams)(),s=(0,f.useRouter)(),{showToast:t}=(0,R.d)(),[r,n]=(0,l.useState)([]),[i,c]=(0,l.useState)([]),[o,d]=(0,l.useState)(new Map),[m,x]=(0,l.useState)(null),[u,g]=(0,l.useState)(""),[p,b]=(0,l.useState)("send"),j=(0,l.useRef)(null),{isConnected:N,isConnecting:y,isWebSocketConnected:k,error:C,connect:S,disconnect:F,sendFile:T,sendFileList:A,requestFile:D,onFileReceived:W,onFileListReceived:E,onFileRequested:L,onFileProgress:z}=function(){let e=function(){let[e,s]=(0,l.useState)({isConnected:!1,isConnecting:!1,isWebSocketConnected:!1,error:null,localDataChannel:null,remoteDataChannel:null}),t=(0,l.useRef)(null),a=(0,l.useRef)(null),r=(0,l.useRef)(null),n=(0,l.useRef)(null),i=[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"}],c=(0,l.useCallback)(e=>{s(s=>({...s,...e}))},[]),o=(0,l.useCallback)(()=>{n.current&&(clearTimeout(n.current),n.current=null)},[]),d=(0,l.useCallback)(()=>{console.warn("WebRTC连接超时"),c({error:"连接超时,请检查取件码是否正确或稍后重试",isConnecting:!1}),t.current&&t.current.close(),a.current&&a.current.close()},[c]),m=(0,l.useCallback)(async(e,s)=>{console.log("=== 开始WebRTC连接 ==="),console.log("房间代码:",e,"角色:",s),o(),c({isConnecting:!0,error:null}),n.current=setTimeout(()=>{d()},3e4);try{let l=new RTCPeerConnection({iceServers:i});a.current=l;let n=new WebSocket("ws://localhost:8080/ws/webrtc?code=".concat(e,"&role=").concat(s));if(t.current=n,n.onopen=async()=>{if(console.log("WebSocket连接已建立"),c({isWebSocketConnected:!0}),"sender"===s)try{let e=await l.createOffer();await l.setLocalDescription(e),n.send(JSON.stringify({type:"offer",payload:e})),console.log("已发送offer")}catch(e){console.error("创建offer失败:",e),c({error:"创建连接失败",isConnecting:!1})}},n.onmessage=async e=>{try{let s=JSON.parse(e.data);switch(console.log("收到信令消息:",s),s.type){case"offer":if(s.payload){await l.setRemoteDescription(new RTCSessionDescription(s.payload));let e=await l.createAnswer();await l.setLocalDescription(e),n.readyState===WebSocket.OPEN&&(n.send(JSON.stringify({type:"answer",payload:e})),console.log("已发送answer"))}break;case"answer":s.payload&&await l.setRemoteDescription(new RTCSessionDescription(s.payload));break;case"ice-candidate":s.payload&&await l.addIceCandidate(new RTCIceCandidate(s.payload));break;case"error":console.error("信令错误:",s.error),c({error:s.error,isConnecting:!1})}}catch(e){console.error("处理信令消息失败:",e),c({error:"信令处理失败",isConnecting:!1})}},n.onerror=e=>{console.error("WebSocket错误:",e),c({error:"WebSocket连接失败",isConnecting:!1,isWebSocketConnected:!1})},n.onclose=()=>{console.log("WebSocket连接已关闭"),c({isWebSocketConnected:!1})},l.onicecandidate=e=>{e.candidate&&n.readyState===WebSocket.OPEN&&(console.log("发送ICE候选:",e.candidate),n.send(JSON.stringify({type:"ice-candidate",payload:e.candidate})))},l.onconnectionstatechange=()=>{console.log("连接状态:",l.connectionState);let e="connected"===l.connectionState;e&&o(),c({isConnected:e,isConnecting:!e&&"failed"!==l.connectionState}),"failed"===l.connectionState&&(o(),c({error:"连接失败",isConnecting:!1}))},"sender"===s){let e=l.createDataChannel("fileTransfer",{ordered:!0,maxPacketLifeTime:void 0,maxRetransmits:void 0});r.current=e,e.bufferedAmountLowThreshold=262144,e.onopen=()=>{console.log("数据通道已打开 (发送方)"),console.log("数据通道配置:",{id:e.id,label:e.label,maxPacketLifeTime:e.maxPacketLifeTime,maxRetransmits:e.maxRetransmits,ordered:e.ordered,bufferedAmountLowThreshold:e.bufferedAmountLowThreshold}),c({localDataChannel:e})}}else l.ondatachannel=e=>{let s=e.channel;r.current=s,console.log("收到数据通道 (接收方)"),s.onopen=()=>{console.log("数据通道已打开 (接收方)"),c({remoteDataChannel:s})}}}catch(e){console.error("连接失败:",e),o(),c({error:e instanceof Error?e.message:"连接失败",isConnecting:!1})}},[c,o,d]),x=(0,l.useCallback)(()=>{console.log("断开WebRTC连接"),o(),r.current&&(r.current.close(),r.current=null),a.current&&(a.current.close(),a.current=null),t.current&&(t.current.close(),t.current=null),s({isConnected:!1,isConnecting:!1,isWebSocketConnected:!1,error:null,localDataChannel:null,remoteDataChannel:null})},[o]),h=(0,l.useCallback)(()=>r.current,[]);return{...e,connect:m,disconnect:x,getDataChannel:h}}(),s=function(){let[e,s]=(0,l.useState)({isTransferring:!1,transferProgress:0,receivedFiles:[],error:null}),t=(0,l.useRef)(new Map),a=(0,l.useRef)(null),r=(0,l.useRef)([]),n=(0,l.useRef)([]),i=(0,l.useRef)([]),c=(0,l.useCallback)(e=>{s(s=>({...s,...e}))},[]),o=(0,l.useCallback)(async(e,s,t)=>{if(!t||"open"!==t.readyState){console.error("数据通道未准备就绪"),c({error:"数据通道未准备就绪"});return}console.log("=== 开始发送文件 ==="),console.log("文件:",e.name,"大小:",e.size,"ID:",s),c({isTransferring:!0,transferProgress:0,error:null});try{let a={id:s,name:e.name,size:e.size,type:e.type},l=JSON.stringify({type:"file-start",payload:a});console.log("发送文件元数据:",l),t.send(l);let r=Math.ceil(e.size/262144);console.log("总分块数:",r);let n=0,o=()=>{if(n>=r){let a=JSON.stringify({type:"file-end",payload:{id:s}});t.send(a),c({isTransferring:!1,transferProgress:100}),console.log("文件发送完成:",e.name);return}let a=262144*n,l=Math.min(a+262144,e.size),d=e.slice(a,l),m=new FileReader;m.onload=a=>{var l;if((null==(l=a.target)?void 0:l.result)&&"open"===t.readyState){let l=a.target.result;if(t.bufferedAmount>1048576){console.log("数据通道缓冲区满,等待清空...");let e=()=>{t.bufferedAmount<262144?d():setTimeout(e,10)};e()}else d();function d(){let a=JSON.stringify({type:"file-chunk",payload:{fileId:s,chunkIndex:n,totalChunks:r}});t.send(a),t.send(l);let d=++n/r*100;c({transferProgress:d}),i.current.forEach(t=>{t({fileId:s,fileName:e.name,progress:d})}),console.log("发送进度: ".concat(d.toFixed(1),"%, 块: ").concat(n,"/").concat(r,", 文件: ").concat(e.name,", 缓冲区: ").concat(t.bufferedAmount," bytes")),o()}}},m.onerror=()=>{console.error("读取文件块失败"),c({error:"读取文件失败",isTransferring:!1})},m.readAsArrayBuffer(d)};o()}catch(e){console.error("发送文件失败:",e),c({error:e instanceof Error?e.message:"发送文件失败",isTransferring:!1})}},[c]),d=(0,l.useCallback)(e=>{if("string"==typeof e.data)try{let l=JSON.parse(e.data);switch(console.log("收到消息:",l.type,l.payload),l.type){case"file-list":console.log("文件列表消息将由主hook处理");return;case"file-start":let i=l.payload;console.log("开始接收文件:",i.name,"大小:",i.size),t.current.set(i.id,{metadata:i,chunks:[],receivedChunks:0,totalChunks:Math.ceil(i.size/262144)}),c({isTransferring:!0,transferProgress:0});break;case"file-chunk":let{fileId:o,chunkIndex:d,totalChunks:m}=l.payload;console.log("接收文件块信息: ".concat(d+1,"/").concat(m,", 文件ID: ").concat(o)),a.current={fileId:o,chunkIndex:d,totalChunks:m};break;case"file-end":let{id:x}=l.payload,h=t.current.get(x);if(h){let e=new Blob(h.chunks,{type:h.metadata.type}),a=new File([e],h.metadata.name,{type:h.metadata.type});console.log("文件接收完成:",a.name),s(e=>({...e,receivedFiles:[...e.receivedFiles,{id:x,file:a}],isTransferring:!1,transferProgress:100})),n.current.forEach(e=>{e({id:x,file:a})}),t.current.delete(x)}break;case"file-request":let{fileId:u,fileName:g}=l.payload;console.log("收到文件请求:",g,"ID:",u),r.current.forEach(e=>{e(u,g)})}}catch(e){console.error("解析消息失败:",e)}else if(e.data instanceof ArrayBuffer){let s=e.data;if(console.log("收到文件块数据:",s.byteLength,"bytes"),a.current){let{fileId:e,chunkIndex:l}=a.current,r=t.current.get(e);if(r){if(!r.chunks[l]){r.chunks[l]=s,r.receivedChunks++;let t=r.receivedChunks/r.totalChunks*100;c({transferProgress:t}),i.current.forEach(s=>{s({fileId:e,fileName:r.metadata.name,progress:t})}),console.log("文件接收进度: ".concat(t.toFixed(1),"%, 块: ").concat(r.receivedChunks,"/").concat(r.totalChunks,", 文件: ").concat(r.metadata.name))}a.current=null}}else console.warn("收到块数据但没有对应的块信息")}},[c]),m=(0,l.useCallback)((e,s,t)=>{if(!t||"open"!==t.readyState)return void console.error("数据通道未准备就绪");console.log("请求文件:",s,"ID:",e);let a=JSON.stringify({type:"file-request",payload:{fileId:e,fileName:s}});t.send(a)},[]),x=(0,l.useCallback)(e=>(r.current.push(e),()=>{let s=r.current.indexOf(e);s>-1&&r.current.splice(s,1)}),[]),h=(0,l.useCallback)(e=>(n.current.push(e),()=>{let s=n.current.indexOf(e);s>-1&&n.current.splice(s,1)}),[]),u=(0,l.useCallback)(e=>(i.current.push(e),()=>{let s=i.current.indexOf(e);s>-1&&i.current.splice(s,1)}),[]);return{...e,sendFile:o,requestFile:m,handleMessage:d,onFileRequested:x,onFileReceived:h,onFileProgress:u}}(),t=(0,l.useRef)([]);(0,l.useEffect)(()=>{let a=e.getDataChannel();if(a&&"open"===a.readyState){console.log("设置数据通道消息处理器");let e=s.handleMessage;a.onmessage=s=>{if(console.log("收到数据通道消息:",typeof s.data),"string"==typeof s.data)try{let e=JSON.parse(s.data);if("file-list"===e.type){console.log("收到文件列表:",e.payload),t.current.forEach(s=>{s(e.payload)});return}}catch(e){console.error("解析文件列表消息失败:",e)}e(s)}}},[e.isConnected,e.getDataChannel,s.handleMessage]);let a=(0,l.useCallback)((t,a)=>{let l=e.getDataChannel();if(!l)return void console.error("数据通道未准备就绪");let r=a||"file_".concat(Date.now());console.log("=== 发送文件 ==="),console.log("文件:",t.name,"ID:",r,"大小:",t.size),s.sendFile(t,r,l)},[e.getDataChannel,s.sendFile]),r=(0,l.useCallback)((t,a)=>{let l=e.getDataChannel();if(!l)return void console.error("数据通道未准备就绪");console.log("=== 请求文件 ==="),console.log("文件:",a,"ID:",t),s.requestFile(t,a,l)},[e.getDataChannel,s.requestFile]),n=(0,l.useCallback)(s=>{let t=e.getDataChannel();if(!t||"open"!==t.readyState)return void console.error("数据通道未准备就绪,无法发送文件列表");console.log("=== 发送文件列表 ==="),console.log("文件列表:",s);let a=JSON.stringify({type:"file-list",payload:s});try{t.send(a),console.log("文件列表已发送")}catch(e){console.error("发送文件列表失败:",e)}},[e.getDataChannel]),i=(0,l.useCallback)(e=>(console.log("注册文件列表回调"),t.current.push(e),()=>{let s=t.current.indexOf(e);s>-1&&t.current.splice(s,1)}),[]);return{isConnected:e.isConnected,isConnecting:e.isConnecting,isWebSocketConnected:e.isWebSocketConnected,error:e.error||s.error,isTransferring:s.isTransferring,transferProgress:s.transferProgress,receivedFiles:s.receivedFiles,connect:e.connect,disconnect:e.disconnect,sendFile:a,requestFile:r,sendFileList:n,onFileRequested:s.onFileRequested,onFileReceived:s.onFileReceived,onFileProgress:s.onFileProgress,onFileListReceived:i}}();(0,l.useEffect)(()=>{let s=e.get("mode"),t=e.get("type"),a=e.get("code");"webrtc"===t&&s&&["send","receive"].includes(s)&&(b(s),a&&"receive"===s&&J(a))},[e]);let P=(0,l.useCallback)(t=>{b(t);let a=new URLSearchParams(e.toString());a.set("type","webrtc"),a.set("mode",t),s.push("?".concat(a.toString()),{scroll:!1})},[e,s]),O=()=>Date.now().toString(36)+Math.random().toString(36).substr(2),M=async()=>{if(0===r.length)return void t("需要选择文件才能创建传输房间","error");try{console.log("=== 创建房间 ==="),console.log("选中文件数:",r.length);let e=await fetch("/api/create-room",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({files:r.map(e=>({name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}))})}),s=await e.json();if(!e.ok)throw Error(s.error||"创建房间失败");let a=s.code;g(a),console.log("房间创建成功,取件码:",a),S(a,"sender"),t("房间创建成功,取件码: ".concat(a),"success")}catch(e){console.error("创建房间失败:",e),t(e instanceof Error?e.message:"网络错误,请重试","error")}},J=e=>{console.log("=== 加入房间 ==="),console.log("取件码:",e),g(e.trim()),S(e.trim(),"receiver"),t("正在连接到房间: ".concat(e),"info")};(0,l.useEffect)(()=>E(e=>{console.log("=== 收到文件列表更新 ==="),console.log("文件列表:",e),console.log("当前模式:",p),"receive"===p&&c(e)}),[E,p]),(0,l.useEffect)(()=>{C&&"receive"===p&&(console.log("=== 连接错误处理 ==="),console.log("错误信息:",C),t("连接失败: ".concat(C),"error"))},[C,p,t]),(0,l.useEffect)(()=>W(e=>{console.log("=== 接收到文件 ==="),console.log("文件:",e.file.name,"ID:",e.id),d(s=>new Map(s.set(e.id,e.file))),c(s=>s.map(s=>s.id===e.id?{...s,status:"completed",progress:100}:s)),t("".concat(e.file.name," 已准备好下载"),"success")}),[W,t]),(0,l.useEffect)(()=>z(e=>{console.log("=== 文件进度更新 ==="),console.log("文件:",e.fileName,"ID:",e.fileId,"进度:",e.progress),x({fileId:e.fileId,fileName:e.fileName,progress:e.progress}),c(s=>s.map(s=>{if(s.id===e.fileId||s.name===e.fileName){let t=e.progress,a=t>=100?"completed":"downloading";return console.log("更新文件 ".concat(s.name," 进度: ").concat(s.progress," -> ").concat(t)),{...s,progress:t,status:a}}return s})),e.progress>=100&&"send"===p&&(t("文件发送完成: ".concat(e.fileName),"success"),x(null))}),[z,p,t]),(0,l.useEffect)(()=>L((e,s)=>{if(console.log("=== 收到文件请求 ==="),console.log("文件:",s,"ID:",e,"当前模式:",p),"send"===p){console.log("当前选中的文件列表:",r.map(e=>e.name));let a=r.find(e=>e.name===s);if(!a){console.error("找不到匹配的文件:",s),console.log("可用文件:",r.map(e=>"".concat(e.name," (").concat(e.size," bytes)"))),t("无法找到文件: ".concat(s),"error");return}console.log("找到匹配文件,开始发送:",a.name,"ID:",e,"文件大小:",a.size),c(t=>t.map(t=>t.id===e||t.name===s?{...t,status:"downloading",progress:0}:t)),T(a,e),t("开始发送文件: ".concat(s),"info")}else console.warn("接收模式下收到文件请求,忽略")}),[L,p,r,T,t]),(0,l.useEffect)(()=>{if(console.log("=== 连接状态变化 ==="),console.log("连接状态:",{isConnected:N,pickupCode:u,mode:p,selectedFilesCount:r.length,fileListCount:i.length}),N&&u&&"send"===p&&r.length>0)if(0===i.length){console.log("创建文件列表并发送...");let e=r.map(e=>({id:O(),name:e.name,size:e.size,type:e.type,status:"ready",progress:0}));c(e),setTimeout(()=>{A(e)},500)}else i.length>0&&(console.log("发送现有文件列表..."),setTimeout(()=>{A(i)},500))},[N,u,p,r.length]);let U=u?"".concat(window.location.origin,"?type=webrtc&mode=receive&code=").concat(u):"";return(0,l.useEffect)(()=>{C&&t(C,"error")},[C,t]),(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsx)("div",{className:"flex justify-center mb-6",children:(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg",children:[(0,a.jsxs)(v,{variant:"send"===p?"default":"ghost",onClick:()=>P("send"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(h.A,{className:"w-4 h-4 mr-2"}),"发送文件"]}),(0,a.jsxs)(v,{variant:"receive"===p?"default":"ghost",onClick:()=>P("receive"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(w.A,{className:"w-4 h-4 mr-2"}),"接收文件"]})]})}),"send"===p?(0,a.jsx)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-4 sm:p-6 shadow-lg border border-white/20 animate-fade-in-up",children:(0,a.jsx)(I,{selectedFiles:r,fileList:i,onFilesChange:n,onGenerateCode:M,pickupCode:u,pickupLink:U,onCopyCode:()=>{navigator.clipboard.writeText(u),t("取件码已复制到剪贴板","success")},onCopyLink:()=>{let e="".concat(window.location.origin,"?type=webrtc&mode=receive&code=").concat(u);navigator.clipboard.writeText(e),t("取件链接已复制到剪贴板","success")},onAddMoreFiles:()=>{var e;null==(e=j.current)||e.click()},onRemoveFile:n,onClearFiles:()=>{console.log("=== 清空文件 ==="),n([]),c([]),N&&u&&A([])},onReset:()=>{console.log("=== 重置房间 ==="),F(),g(""),n([]),c([]),d(new Map)},disabled:!!m,isConnected:N,isWebSocketConnected:k})}):(0,a.jsx)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-4 sm:p-6 shadow-lg border border-white/20 animate-fade-in-up",children:(0,a.jsx)(B,{onJoinRoom:J,files:i,onDownloadFile:e=>{o.get(e)?(e=>{let s=o.get(e);if(!s)return;let a=URL.createObjectURL(s),l=document.createElement("a");l.href=a,l.download=s.name,document.body.appendChild(l),l.click(),document.body.removeChild(l),URL.revokeObjectURL(a),t("".concat(s.name," 已保存到下载文件夹"),"success")})(e):(e=>{if("receive"!==p)return console.error("requestFile只能在接收模式下调用");let s=i.find(s=>s.id===e);if(!s)return console.error("找不到文件信息:",e);console.log("=== 开始请求文件 ==="),console.log("文件信息:",{name:s.name,id:e,size:s.size}),console.log("当前文件状态:",s.status),console.log("WebRTC连接状态:",{isConnected:N,isTransferring:!!m}),c(s=>{let t=s.map(s=>s.id===e?{...s,status:"downloading",progress:0}:s);return console.log("更新后的文件列表:",t.find(s=>s.id===e)),t}),console.log("调用hook的requestFile..."),D(e,s.name),t("正在请求文件: ".concat(s.name),"info")})(e)},isConnected:N,isConnecting:y,isWebSocketConnected:k,downloadedFiles:o,error:C,onReset:()=>{if(console.log("=== 重置连接状态 ==="),F(),g(""),c([]),d(new Map),"receive"===p){let t=new URLSearchParams(e.toString());t.delete("code"),s.push("?".concat(t.toString()),{scroll:!1})}}})}),(0,a.jsx)("input",{ref:j,type:"file",multiple:!0,onChange:e=>(e=>{console.log("=== 文件选择 ==="),console.log("新文件:",e.map(e=>e.name)),n(s=>[...s,...e]);let s=e.map(e=>({id:O(),name:e.name,size:e.size,type:e.type,status:"ready",progress:0}));c(e=>{let t=[...e,...s];return console.log("更新后的文件列表:",t),N&&u&&(console.log("立即同步文件列表到对端"),setTimeout(()=>A(t),100)),t})})(Array.from(e.target.files||[])),className:"hidden"})]})};function U(){return(0,a.jsx)("div",{className:"min-h-screen bg-gradient-to-br from-slate-50 to-blue-50",children:(0,a.jsxs)("div",{className:"container mx-auto px-4 py-4 sm:py-6 md:py-8",children:[(0,a.jsx)("div",{className:"text-center mb-6 sm:mb-8",children:(0,a.jsx)(p,{})}),(0,a.jsx)("div",{className:"max-w-4xl mx-auto",children:(0,a.jsxs)(o,{defaultValue:"webrtc",className:"w-full",children:[(0,a.jsx)("div",{className:"mb-6",children:(0,a.jsxs)(d,{className:"grid w-full grid-cols-3 max-w-lg mx-auto h-auto bg-white/90 backdrop-blur-sm shadow-lg rounded-xl p-2 border border-slate-200",children:[(0,a.jsxs)(m,{value:"webrtc",className:"flex items-center justify-center space-x-2 px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 hover:bg-slate-50 data-[state=active]:bg-blue-500 data-[state=active]:text-white data-[state=active]:shadow-md data-[state=active]:hover:bg-blue-600",children:[(0,a.jsx)(h.A,{className:"w-4 h-4"}),(0,a.jsx)("span",{className:"hidden sm:inline",children:"文件传输"}),(0,a.jsx)("span",{className:"sm:hidden",children:"文件"})]}),(0,a.jsxs)(m,{value:"text",className:"flex items-center justify-center space-x-2 px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 hover:bg-slate-50 data-[state=active]:bg-emerald-500 data-[state=active]:text-white data-[state=active]:shadow-md data-[state=active]:hover:bg-emerald-600",children:[(0,a.jsx)(u.A,{className:"w-4 h-4"}),(0,a.jsx)("span",{className:"hidden sm:inline",children:"文本传输"}),(0,a.jsx)("span",{className:"sm:hidden",children:"文本"})]}),(0,a.jsxs)(m,{value:"desktop",className:"flex items-center justify-center space-x-2 px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 hover:bg-slate-50 data-[state=active]:bg-purple-500 data-[state=active]:text-white data-[state=active]:shadow-md data-[state=active]:hover:bg-purple-600",children:[(0,a.jsx)(g.A,{className:"w-4 h-4"}),(0,a.jsx)("span",{className:"hidden sm:inline",children:"共享桌面"}),(0,a.jsx)("span",{className:"sm:hidden",children:"桌面"})]})]})}),(0,a.jsxs)("div",{children:[(0,a.jsx)(x,{value:"webrtc",className:"mt-0 animate-fade-in-up",children:(0,a.jsx)(J,{})}),(0,a.jsx)(x,{value:"text",className:"mt-0 animate-fade-in-up",children:(0,a.jsx)(T,{onSendText:async e=>{try{let s=await fetch("/api/create-text-room",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:e})}),t=await s.json();if(!s.ok)throw Error(t.error||"创建文本房间失败");return t.code}catch(e){throw console.error("创建文本房间失败:",e),e}},onReceiveText:async e=>{try{let s=await fetch("/api/get-text-content?code=".concat(e)),t=await s.json();if(!s.ok)throw Error(t.error||"获取文本内容失败");return t.text}catch(e){throw console.error("获取文本内容失败:",e),e}}})}),(0,a.jsx)(x,{value:"desktop",className:"mt-0 animate-fade-in-up",children:(0,a.jsx)(E,{})})]})]})})]})})}let _=function(){return(0,a.jsx)(l.Suspense,{fallback:(0,a.jsx)("div",{className:"min-h-screen flex items-center justify-center",children:"加载中..."}),children:(0,a.jsx)(U,{})})}},6801:(e,s,t)=>{"use strict";t.d(s,{ToastProvider:()=>i,d:()=>n});var a=t(5155),l=t(2115);let r=(0,l.createContext)(void 0),n=()=>{let e=(0,l.useContext)(r);if(!e)throw Error("useToast must be used within a ToastProvider");return e},i=e=>{let{children:s}=e,[t,n]=(0,l.useState)([]),i=(0,l.useCallback)(function(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"success",t=Date.now().toString(),a={id:t,message:e,type:s};n(e=>[...e,a]),setTimeout(()=>{n(e=>e.filter(e=>e.id!==t))},3e3)},[]),c=(0,l.useCallback)(e=>{n(s=>s.filter(s=>s.id!==e))},[]);return(0,a.jsxs)(r.Provider,{value:{showToast:i},children:[s,(0,a.jsx)("div",{className:"fixed top-4 left-1/2 transform -translate-x-1/2 z-50 space-y-2",children:t.map(e=>(0,a.jsx)("div",{className:"\n max-w-sm p-4 rounded-xl shadow-lg backdrop-blur-sm transform transition-all duration-300 ease-in-out\n ".concat("success"===e.type?"bg-emerald-50/90 border border-emerald-200 text-emerald-800":"","\n ").concat("error"===e.type?"bg-red-50/90 border border-red-200 text-red-800":"","\n ").concat("info"===e.type?"bg-blue-50/90 border border-blue-200 text-blue-800":"","\n animate-slide-in-down\n "),onClick:()=>c(e.id),children:(0,a.jsxs)("div",{className:"flex items-center space-x-3",children:[(0,a.jsxs)("div",{className:"flex-shrink-0",children:["success"===e.type&&(0,a.jsx)("div",{className:"w-6 h-6 bg-emerald-500 rounded-full flex items-center justify-center",children:(0,a.jsx)("svg",{className:"w-4 h-4 text-white",fill:"currentColor",viewBox:"0 0 20 20",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",clipRule:"evenodd"})})}),"error"===e.type&&(0,a.jsx)("div",{className:"w-6 h-6 bg-red-500 rounded-full flex items-center justify-center",children:(0,a.jsx)("svg",{className:"w-4 h-4 text-white",fill:"currentColor",viewBox:"0 0 20 20",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})}),"info"===e.type&&(0,a.jsx)("div",{className:"w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center",children:(0,a.jsx)("svg",{className:"w-4 h-4 text-white",fill:"currentColor",viewBox:"0 0 20 20",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z",clipRule:"evenodd"})})})]}),(0,a.jsx)("p",{className:"text-sm font-medium",children:e.message})]})},e.id))})]})}}},e=>{e.O(0,[423,441,964,358],()=>e(e.s=409)),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/internal/web/frontend/_next/static/chunks/app/page-c710cf440dafbd05.js b/internal/web/frontend/_next/static/chunks/app/page-c710cf440dafbd05.js
new file mode 100644
index 0000000..2dd05c7
--- /dev/null
+++ b/internal/web/frontend/_next/static/chunks/app/page-c710cf440dafbd05.js
@@ -0,0 +1 @@
+(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[974],{409:(e,s,t)=>{Promise.resolve().then(t.bind(t,7443))},6801:(e,s,t)=>{"use strict";t.d(s,{ToastProvider:()=>o,d:()=>n});var a=t(5155),l=t(2115);let r=(0,l.createContext)(void 0),n=()=>{let e=(0,l.useContext)(r);if(!e)throw Error("useToast must be used within a ToastProvider");return e},o=e=>{let{children:s}=e,[t,n]=(0,l.useState)([]),o=(0,l.useCallback)(function(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"success",t=Date.now().toString(),a={id:t,message:e,type:s};n(e=>[...e,a]),setTimeout(()=>{n(e=>e.filter(e=>e.id!==t))},3e3)},[]),i=(0,l.useCallback)(e=>{n(s=>s.filter(s=>s.id!==e))},[]);return(0,a.jsxs)(r.Provider,{value:{showToast:o},children:[s,(0,a.jsx)("div",{className:"fixed top-4 left-1/2 transform -translate-x-1/2 z-50 space-y-2",children:t.map(e=>(0,a.jsx)("div",{className:"\n max-w-sm p-4 rounded-xl shadow-lg backdrop-blur-sm transform transition-all duration-300 ease-in-out\n ".concat("success"===e.type?"bg-emerald-50/90 border border-emerald-200 text-emerald-800":"","\n ").concat("error"===e.type?"bg-red-50/90 border border-red-200 text-red-800":"","\n ").concat("info"===e.type?"bg-blue-50/90 border border-blue-200 text-blue-800":"","\n animate-slide-in-down\n "),onClick:()=>i(e.id),children:(0,a.jsxs)("div",{className:"flex items-center space-x-3",children:[(0,a.jsxs)("div",{className:"flex-shrink-0",children:["success"===e.type&&(0,a.jsx)("div",{className:"w-6 h-6 bg-emerald-500 rounded-full flex items-center justify-center",children:(0,a.jsx)("svg",{className:"w-4 h-4 text-white",fill:"currentColor",viewBox:"0 0 20 20",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z",clipRule:"evenodd"})})}),"error"===e.type&&(0,a.jsx)("div",{className:"w-6 h-6 bg-red-500 rounded-full flex items-center justify-center",children:(0,a.jsx)("svg",{className:"w-4 h-4 text-white",fill:"currentColor",viewBox:"0 0 20 20",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z",clipRule:"evenodd"})})}),"info"===e.type&&(0,a.jsx)("div",{className:"w-6 h-6 bg-blue-500 rounded-full flex items-center justify-center",children:(0,a.jsx)("svg",{className:"w-4 h-4 text-white",fill:"currentColor",viewBox:"0 0 20 20",children:(0,a.jsx)("path",{fillRule:"evenodd",d:"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z",clipRule:"evenodd"})})})]}),(0,a.jsx)("p",{className:"text-sm font-medium",children:e.message})]})},e.id))})]})}},7443:(e,s,t)=>{"use strict";t.d(s,{default:()=>V});var a=t(5155),l=t(2115),r=t(5695),n=t(3559),o=t(2596),i=t(9688);function c(){for(var e=arguments.length,s=Array(e),t=0;t{let{className:t,...l}=e;return(0,a.jsx)(n.B8,{ref:s,className:c("inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",t),...l})});m.displayName=n.B8.displayName;let x=l.forwardRef((e,s)=>{let{className:t,...l}=e;return(0,a.jsx)(n.l9,{ref:s,className:c("inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",t),...l})});x.displayName=n.l9.displayName;let u=l.forwardRef((e,s)=>{let{className:t,...l}=e;return(0,a.jsx)(n.UC,{ref:s,className:c("mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",t),...l})});u.displayName=n.UC.displayName;var h=t(9869),g=t(1497),p=t(4738);function f(){return(0,a.jsxs)("div",{className:"text-center mb-6 animate-fade-in-up",children:[(0,a.jsx)("h1",{className:"text-2xl sm:text-3xl md:text-4xl font-bold bg-gradient-to-r from-blue-600 via-purple-600 to-indigo-600 bg-clip-text text-transparent mb-2",children:"文件快传"}),(0,a.jsxs)("p",{className:"text-sm sm:text-base text-slate-600 max-w-xl mx-auto leading-relaxed px-4 mb-3",children:["安全、快速、简单的传输服务",(0,a.jsx)("br",{}),(0,a.jsx)("span",{className:"text-xs sm:text-sm text-slate-500",children:"支持文件、文字、桌面共享 - 无需注册,即传即用"})]}),(0,a.jsx)("div",{className:"w-64 sm:w-80 md:w-96 lg:w-[32rem] xl:w-[40rem] h-0.5 bg-gradient-to-r from-blue-400 via-purple-400 to-indigo-400 mx-auto mt-4 mb-2 opacity-60"})]})}var b=t(9708);let v=(0,t(2085).F)("inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-destructive-foreground hover:bg-destructive/90",outline:"border border-input bg-background hover:bg-accent hover:text-accent-foreground",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-10 px-4 py-2",sm:"h-9 rounded-md px-3",lg:"h-11 rounded-md px-8",icon:"h-10 w-10"}},defaultVariants:{variant:"default",size:"default"}}),j=l.forwardRef((e,s)=>{let{className:t,variant:l,size:r,asChild:n=!1,...o}=e,i=n?b.DX:"button";return(0,a.jsx)(i,{className:c(v({variant:l,size:r,className:t})),ref:s,...o})});j.displayName="Button";let N=l.forwardRef((e,s)=>{let{className:t,type:l,...r}=e;return(0,a.jsx)("input",{type:l,className:c("flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",t),ref:s,...r})});N.displayName="Input";var w=t(1788),y=t(2486),C=t(4357),k=t(8164),S=t(7213),R=t(2657),T=t(6801);function F(e){let{onSendText:s,onReceiveText:t,websocket:n,isConnected:o=!1,currentRole:i,pickupCode:c,onCreateWebSocket:d}=e,m=(0,r.useSearchParams)(),x=(0,r.useRouter)(),[u,h]=(0,l.useState)("send"),[p,f]=(0,l.useState)(""),[b,v]=(0,l.useState)(""),[F,E]=(0,l.useState)(""),[A,D]=(0,l.useState)(!1),[W,P]=(0,l.useState)(!1),[L,I]=(0,l.useState)(0),[z,O]=(0,l.useState)([]),[U,M]=(0,l.useState)([]),[B,_]=(0,l.useState)(null),[J,q]=(0,l.useState)(null),[G,V]=(0,l.useState)(!1),{showToast:K}=(0,T.d)(),X=(0,l.useRef)(null),H=(0,l.useRef)(null),Q=(0,l.useRef)(null);(0,l.useEffect)(()=>{let e=m.get("mode");if("text"===m.get("type")&&e&&["send","receive"].includes(e)){h(e);let s=m.get("code");"receive"===e&&s&&6===s.length&&v(s.toUpperCase())}},[m]),(0,l.useEffect)(()=>{let e=e=>{var s,t,a,l,r,n;let o=e.detail;switch(console.log("TextTransfer收到WebSocket消息:",o),o.type){case"text-content":(null==(s=o.payload)?void 0:s.text)!==void 0&&(E(o.payload.text),"receiver"===i&&(f(o.payload.text),!G&&A&&(V(!0),K("成功加入文字房间!","success"))),Q.current&&(clearTimeout(Q.current),Q.current=null),A&&D(!1));break;case"text-update":(null==(t=o.payload)?void 0:t.text)!==void 0&&(E(o.payload.text),"receiver"===i&&f(o.payload.text));break;case"text-send":(null==(a=o.payload)?void 0:a.text)&&(E(o.payload.text),K("收到新的文字内容!","success"));break;case"image-send":(null==(l=o.payload)?void 0:l.imageData)&&(console.log("接收到图片数据:",o.payload.imageData.substring(0,100)+"..."),o.payload.imageData.startsWith("data:image/")?(M(e=>[...e,o.payload.imageData]),K("收到新的图片!","success")):(console.error("无效的图片数据格式:",o.payload.imageData.substring(0,50)),K("收到的图片格式不正确","error")));break;case"room-status":(null==(r=o.payload)?void 0:r.sender_count)!==void 0&&(null==(n=o.payload)?void 0:n.receiver_count)!==void 0&&I(o.payload.sender_count+o.payload.receiver_count)}},s=e=>{let{code:s,reason:t}=e.detail;console.log("WebSocket连接关闭:",s,t),A&&(D(!1),1e3!==s&&K("取件码不存在或已过期","error"))},t=e=>{console.error("WebSocket连接错误:",e.detail),A&&(D(!1),K("取件码不存在或已过期","error"))};return window.addEventListener("websocket-message",e),window.addEventListener("websocket-close",s),window.addEventListener("websocket-error",t),()=>{window.removeEventListener("websocket-message",e),window.removeEventListener("websocket-close",s),window.removeEventListener("websocket-error",t),Q.current&&clearTimeout(Q.current)}},[i,K,G,A]);let Z=(0,l.useCallback)(e=>{h(e);let s=new URLSearchParams(m.toString());s.set("type","text"),s.set("mode",e),x.push("?".concat(s.toString()),{scroll:!1})},[m,x]),$=(0,l.useCallback)(e=>{n&&o&&(H.current&&clearTimeout(H.current),H.current=setTimeout(()=>{n.send(JSON.stringify({type:"text-update",payload:{text:e}}))},300))},[n,o]),Y=(0,l.useCallback)(e=>{let s=e.target.value;f(s),o&&n&&$(s)},[o,n,$]),ee=(0,l.useCallback)(async()=>{if(!p.trim())return void K("请输入要传输的文字内容","error");D(!0);try{if(s){let e=await s(p);e&&(v(e),P(!0),K("房间创建成功!","success"),d&&d(e,"sender"))}}catch(e){console.error("创建房间失败:",e)}finally{D(!1)}},[p,s,d,K]),es=(0,l.useCallback)(async()=>{if(!b.trim()||6!==b.length)return void K("请输入正确的6位房间码","error");if(!A){D(!0),V(!1);try{let e=await fetch("/api/room-info?code=".concat(b)),s=await e.json();if(!e.ok||!s.success){K(s.message||"房间不存在或已过期","error"),D(!1);return}d&&(console.log("房间验证成功,手动加入房间:",b),d(b,"receiver"),Q.current=setTimeout(()=>{A&&(D(!1),K("取件码不存在或已过期","error"))},8e3))}catch(e){console.error("加入房间失败:",e),K("网络错误,请稍后重试","error"),D(!1)}}},[b,d,K,A]),et=(0,l.useCallback)(()=>{n&&o&&p.trim()&&(n.send(JSON.stringify({type:"text-send",payload:{text:p}})),K("文字已发送!","success"))},[n,o,p,K]),ea=(0,l.useCallback)(e=>new Promise((s,t)=>{let a=document.createElement("canvas"),l=a.getContext("2d"),r=document.createElement("img");if(!l)return void t(Error("无法创建Canvas上下文"));r.onload=()=>{try{let{width:e,height:t}=r;e>t?e>800&&(t=800*t/e,e=800):t>600&&(e=600*e/t,t=600),a.width=e,a.height=t,l.fillStyle="#FFFFFF",l.fillRect(0,0,e,t),l.drawImage(r,0,0,e,t);let n=a.toDataURL("image/jpeg",.8);console.log("图片压缩完成,数据长度:",n.length,"前100字符:",n.substring(0,100)),s(n)}catch(e){t(Error("图片压缩失败: "+e))}},r.onerror=()=>t(Error("图片加载失败"));let n=new FileReader;n.onload=e=>{var s;(null==(s=e.target)?void 0:s.result)?r.src=e.target.result:t(Error("文件读取失败"))},n.onerror=()=>t(Error("文件读取失败")),n.readAsDataURL(e)}),[]),el=(0,l.useCallback)(async e=>{var s;let t=null==(s=e.clipboardData)?void 0:s.items;if(t)for(let e=0;e[...e,s]),n&&o&&(n.send(JSON.stringify({type:"image-send",payload:{imageData:s}})),K("图片已发送!","success"))}catch(e){console.error("图片处理失败:",e),K("图片处理失败,请重试","error")}}}},[n,o,K,ea]),er=(0,l.useCallback)(async e=>{try{await navigator.clipboard.writeText(e),K("已复制到剪贴板!","success")}catch(e){K("复制失败","error")}},[K]),en=(0,l.useCallback)(async e=>{let s=window.location.origin+window.location.pathname,t="".concat(s,"?type=text&mode=receive&code=").concat(e);await er(t)},[er]),eo=(0,l.useCallback)((e,s)=>{let t=document.createElement("a");t.download="image_".concat(s+1,".jpg"),t.href=e,document.body.appendChild(t),t.click(),document.body.removeChild(t),K("图片已下载!","success")},[K]);return(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsx)("div",{className:"flex justify-center mb-6",children:(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg",children:[(0,a.jsxs)(j,{variant:"send"===u?"default":"ghost",onClick:()=>Z("send"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(y.A,{className:"w-4 h-4 mr-2"}),"发送文字"]}),(0,a.jsxs)(j,{variant:"receive"===u?"default":"ghost",onClick:()=>Z("receive"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(w.A,{className:"w-4 h-4 mr-2"}),"加入房间"]})]})}),"send"===u?(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(g.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"传送文字"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:W?"实时编辑,对方可以同步看到":"输入要传输的文本内容"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:W?o?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-red-500"}),(0,a.jsx)("span",{className:"text-red-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})]}),L>0&&(0,a.jsxs)("div",{className:"mt-1 text-xs text-blue-600",children:[L," 人在线"]})]})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)("textarea",{ref:X,value:p,onChange:Y,onPaste:el,placeholder:"在这里输入要传输的文本内容... \uD83D\uDCA1 提示:支持实时同步编辑,可以直接粘贴图片 (Ctrl+V)",className:"w-full min-h-[150px] p-4 border-2 border-slate-200 rounded-xl focus:border-blue-500 focus:ring-blue-500 bg-white/80 backdrop-blur-sm resize-none",disabled:A}),W&&o&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-emerald-100 text-emerald-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-emerald-500 rounded-full animate-pulse"}),(0,a.jsx)("span",{children:"实时同步"})]})}),W&&!o&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-orange-100 text-orange-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-orange-500 rounded-full"}),(0,a.jsx)("span",{children:"连接中..."})]})})]}),(0,a.jsxs)("div",{className:"flex justify-between text-sm text-slate-500",children:[(0,a.jsxs)("span",{children:[p.length," 字符"]}),(0,a.jsx)("span",{children:"最大 50,000 字符"})]}),W?(0,a.jsx)("div",{className:"space-y-4",children:(0,a.jsx)("div",{className:"p-4 bg-gradient-to-r from-emerald-50 to-teal-50 rounded-xl border border-emerald-200",children:(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)("p",{className:"text-sm text-emerald-700 mb-2",children:"房间码"}),(0,a.jsx)("div",{className:"text-2xl font-bold font-mono text-emerald-600 mb-3",children:b}),(0,a.jsxs)("div",{className:"flex flex-wrap gap-2 justify-center",children:[(0,a.jsxs)(j,{onClick:()=>er(b),size:"sm",className:"bg-emerald-500 hover:bg-emerald-600 text-white",children:[(0,a.jsx)(C.A,{className:"w-4 h-4 mr-2"}),"复制房间码"]}),(0,a.jsxs)(j,{onClick:()=>en(b),size:"sm",className:"bg-purple-500 hover:bg-purple-600 text-white",children:[(0,a.jsx)(k.A,{className:"w-4 h-4 mr-2"}),"复制链接"]}),(0,a.jsxs)(j,{onClick:et,size:"sm",className:"bg-blue-500 hover:bg-blue-600 text-white",disabled:!p.trim(),children:[(0,a.jsx)(y.A,{className:"w-4 h-4 mr-2"}),"发送文字"]})]})]})})}):(0,a.jsx)(j,{onClick:ee,disabled:!p.trim()||p.length>5e4||A,className:"w-full h-12 bg-gradient-to-r from-blue-500 to-indigo-500 hover:from-blue-600 hover:to-indigo-600 text-white text-lg font-medium rounded-xl shadow-lg",children:A?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"创建房间..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(y.A,{className:"w-5 h-5 mr-2"}),"创建文字传输房间"]})}),"send"===u&&z.length>0&&(0,a.jsxs)("div",{className:"mt-6",children:[(0,a.jsxs)("h3",{className:"text-lg font-medium text-slate-800 mb-3 flex items-center",children:[(0,a.jsx)(S.A,{className:"w-5 h-5 mr-2"}),"已发送的图片 (",z.length,")"]}),(0,a.jsx)("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-3",children:z.map((e,s)=>(0,a.jsxs)("div",{className:"relative group overflow-hidden",children:[(0,a.jsx)("img",{src:e,alt:"图片 ".concat(s+1),className:"w-full h-24 object-cover rounded-lg border-2 border-slate-200 hover:border-blue-400 transition-all duration-200 cursor-pointer bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",onClick:()=>q(e),onError:s=>{console.error("图片加载失败:",e),s.currentTarget.style.display="none"}}),(0,a.jsx)("div",{className:"absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-opacity rounded-lg"}),(0,a.jsxs)("div",{className:"absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1",children:[(0,a.jsx)("button",{onClick:s=>{s.stopPropagation(),q(e)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"预览图片",children:(0,a.jsx)(R.A,{className:"w-3.5 h-3.5 text-slate-600"})}),(0,a.jsx)("button",{onClick:t=>{t.stopPropagation(),eo(e,s)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"下载图片",children:(0,a.jsx)(w.A,{className:"w-3.5 h-3.5 text-slate-600"})})]}),(0,a.jsx)("div",{className:"absolute bottom-1 left-1 bg-black bg-opacity-50 text-white text-xs px-1.5 py-0.5 rounded",children:s+1})]},s))})]})]})]}):(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"加入房间"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:F||p?o?"已连接,可以实时查看和编辑":"连接断开,等待重连":"输入6位房间码来获取文字内容"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:F||p?o?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-red-500"}),(0,a.jsx)("span",{className:"text-red-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})]}),L>0&&(0,a.jsxs)("div",{className:"mt-1 text-xs text-blue-600",children:[L," 人在线"]})]})]}),(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)(N,{value:b,onChange:e=>v(e.target.value.toUpperCase().slice(0,6)),placeholder:"请输入房间码",className:"text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-emerald-500 focus:ring-emerald-500 bg-white/80 backdrop-blur-sm",maxLength:6,disabled:A}),(0,a.jsx)(j,{onClick:es,disabled:6!==b.length||A,className:"w-full h-12 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white text-lg font-medium rounded-xl shadow-lg",children:A?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"连接中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(w.A,{className:"w-5 h-5 mr-2"}),"加入房间"]})}),(F||p)&&(0,a.jsxs)("div",{className:"mt-6 space-y-4",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)("textarea",{value:F||p,readOnly:"receiver"!==i,onChange:"receiver"===i?Y:void 0,className:"w-full min-h-[150px] p-4 border-2 border-emerald-200 rounded-xl bg-emerald-50/50 backdrop-blur-sm resize-none"}),"receiver"===i&&o&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-emerald-100 text-emerald-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-emerald-500 rounded-full animate-pulse"}),(0,a.jsx)("span",{children:"实时同步"})]})}),"receiver"===i&&!o&&(0,a.jsx)("div",{className:"absolute top-2 right-2",children:(0,a.jsxs)("div",{className:"flex items-center space-x-1 bg-orange-100 text-orange-700 px-2 py-1 rounded-lg text-xs",children:[(0,a.jsx)("div",{className:"w-2 h-2 bg-orange-500 rounded-full"}),(0,a.jsx)("span",{children:"连接中..."})]})})]}),(0,a.jsxs)(j,{onClick:()=>er(F||p),className:"w-full h-12 bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white text-lg font-medium rounded-xl shadow-lg",children:[(0,a.jsx)(C.A,{className:"w-5 h-5 mr-2"}),"复制文字"]})]}),"receive"===u&&U.length>0&&(0,a.jsxs)("div",{className:"mt-6",children:[(0,a.jsxs)("h3",{className:"text-lg font-medium text-slate-800 mb-3 flex items-center",children:[(0,a.jsx)(S.A,{className:"w-5 h-5 mr-2"}),"接收到的图片 (",U.length,")"]}),(0,a.jsx)("div",{className:"grid grid-cols-2 sm:grid-cols-3 gap-3",children:U.map((e,s)=>(0,a.jsxs)("div",{className:"relative group overflow-hidden",children:[(0,a.jsx)("img",{src:e,alt:"图片 ".concat(s+1),className:"w-full h-24 object-cover rounded-lg border-2 border-slate-200 hover:border-emerald-400 transition-all duration-200 cursor-pointer bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",onClick:()=>q(e),onLoad:e=>{console.log("图片 ".concat(s+1," 加载成功"))},onError:t=>{console.error("图片 ".concat(s+1," 加载失败:"),e.substring(0,100)),t.currentTarget.style.backgroundColor="#f1f5f9",t.currentTarget.style.display="flex",t.currentTarget.style.alignItems="center",t.currentTarget.style.justifyContent="center",t.currentTarget.innerHTML='图片加载失败 '}}),(0,a.jsx)("div",{className:"absolute inset-0 bg-black bg-opacity-0 group-hover:bg-opacity-20 transition-opacity rounded-lg"}),(0,a.jsxs)("div",{className:"absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1",children:[(0,a.jsx)("button",{onClick:s=>{s.stopPropagation(),q(e)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"预览图片",children:(0,a.jsx)(R.A,{className:"w-3.5 h-3.5 text-slate-600"})}),(0,a.jsx)("button",{onClick:t=>{t.stopPropagation(),eo(e,s)},className:"p-1.5 bg-white bg-opacity-90 hover:bg-opacity-100 rounded-md shadow-sm transition-all hover:scale-105",title:"下载图片",children:(0,a.jsx)(w.A,{className:"w-3.5 h-3.5 text-slate-600"})})]}),(0,a.jsx)("div",{className:"absolute bottom-1 left-1 bg-black bg-opacity-50 text-white text-xs px-1.5 py-0.5 rounded",children:s+1})]},s))})]})]})]}),J&&(0,a.jsx)(e=>{let{src:s,onClose:t}=e;return(0,a.jsx)("div",{className:"fixed inset-0 bg-slate-900/80 backdrop-blur-sm flex items-center justify-center z-50 p-4 animate-fade-in",onClick:t,children:(0,a.jsx)("div",{className:"relative max-w-[90vw] max-h-[90vh] animate-scale-in",children:(0,a.jsxs)("div",{className:"relative bg-white rounded-2xl overflow-hidden shadow-2xl",children:[(0,a.jsx)("img",{src:s,alt:"预览",className:"max-w-full max-h-[80vh] object-contain block bg-gradient-to-br from-slate-50 via-blue-50 to-indigo-50",onClick:e=>e.stopPropagation(),onError:e=>{console.error("预览图片加载失败:",s)}}),(0,a.jsx)("div",{className:"absolute top-0 left-0 right-0 bg-gradient-to-b from-slate-900/60 to-transparent p-4",children:(0,a.jsxs)("div",{className:"flex justify-between items-center",children:[(0,a.jsx)("h3",{className:"text-white font-medium text-lg",children:"图片预览"}),(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)("button",{onClick:e=>{e.stopPropagation();let t=z.indexOf(s);-1===t&&(t=U.indexOf(s)),eo(s,t>=0?t:0)},className:"bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white p-2 rounded-lg shadow-lg transition-all hover:scale-105",title:"下载图片",children:(0,a.jsx)(w.A,{className:"w-5 h-5"})}),(0,a.jsx)("button",{onClick:t,className:"bg-white/20 hover:bg-white/30 backdrop-blur-sm text-white p-2 rounded-lg shadow-lg transition-all hover:scale-105",title:"关闭预览",children:(0,a.jsx)("svg",{className:"w-5 h-5",fill:"none",stroke:"currentColor",viewBox:"0 0 24 24",children:(0,a.jsx)("path",{strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:2,d:"M6 18L18 6M6 6l12 12"})})})]})]})}),(0,a.jsx)("div",{className:"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-slate-900/60 to-transparent p-4",children:(0,a.jsx)("div",{className:"text-white text-sm opacity-80",children:"点击空白区域关闭预览"})})]})})})},{src:J,onClose:()=>q(null)})]})}var E=t(6683),A=t(5690),D=t(8979);function W(e){let{onStartSharing:s,onStopSharing:t,onJoinSharing:n}=e,o=(0,r.useSearchParams)(),i=(0,r.useRouter)(),[c,d]=(0,l.useState)("share"),[m,x]=(0,l.useState)(""),[u,h]=(0,l.useState)(""),[g,f]=(0,l.useState)(!1),[b,v]=(0,l.useState)(!1),[w,y]=(0,l.useState)(!1),{showToast:k}=(0,T.d)();(0,l.useEffect)(()=>{let e=o.get("mode");"desktop"===o.get("type")&&e&&("send"===e?d("share"):"receive"===e&&d("view"))},[o]);let S=(0,l.useCallback)(e=>{d(e);let s=new URLSearchParams(o.toString());s.set("type","desktop"),s.set("mode","share"===e?"send":"receive"),i.push("?".concat(s.toString()),{scroll:!1})},[o,i]),R=(0,l.useCallback)(async()=>{if(s){y(!0);try{let e=await s();x(e),f(!0),k("桌面共享已开始!","success")}catch(e){console.error("开始共享失败:",e),k("开始共享失败,请重试","error")}finally{y(!1)}}},[s,k]),F=(0,l.useCallback)(async()=>{if(t){y(!0);try{await t(),f(!1),x(""),k("桌面共享已停止","success")}catch(e){console.error("停止共享失败:",e),k("停止共享失败","error")}finally{y(!1)}}},[t,k]),W=(0,l.useCallback)(async()=>{if(u.trim()&&n){y(!0);try{await n(u),v(!0),k("已连接到桌面共享!","success")}catch(e){console.error("连接失败:",e),k("连接失败,请检查连接码","error")}finally{y(!1)}}},[u,n,k]),P=(0,l.useCallback)(async e=>{try{await navigator.clipboard.writeText(e),k("已复制到剪贴板!","success")}catch(e){k("复制失败","error")}},[k]);return(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsx)("div",{className:"flex justify-center mb-6",children:(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg",children:[(0,a.jsxs)(j,{variant:"share"===c?"default":"ghost",onClick:()=>S("share"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(E.A,{className:"w-4 h-4 mr-2"}),"共享桌面"]}),(0,a.jsxs)(j,{variant:"view"===c?"default":"ghost",onClick:()=>S("view"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(p.A,{className:"w-4 h-4 mr-2"}),"观看桌面"]})]})}),"share"===c?(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-purple-500 to-pink-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(E.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"共享桌面"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:g?"桌面共享进行中":"开始共享您的桌面屏幕"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:g?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]}),g&&m&&(0,a.jsx)("div",{className:"mt-1 text-xs text-purple-600",children:m})]})]}),(0,a.jsx)("div",{className:"space-y-4",children:g?(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)("div",{className:"p-4 bg-gradient-to-r from-purple-50 to-pink-50 rounded-xl border border-purple-200",children:(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)("p",{className:"text-sm text-purple-700 mb-2",children:"连接码"}),(0,a.jsx)("div",{className:"text-2xl font-bold font-mono text-purple-600 mb-3",children:m}),(0,a.jsxs)(j,{onClick:()=>P(m),size:"sm",className:"bg-purple-500 hover:bg-purple-600 text-white",children:[(0,a.jsx)(C.A,{className:"w-4 h-4 mr-2"}),"复制连接码"]})]})}),(0,a.jsx)(j,{onClick:F,disabled:w,className:"w-full h-12 bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white text-lg font-medium rounded-xl shadow-lg",children:w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"停止中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(D.A,{className:"w-5 h-5 mr-2"}),"停止共享"]})})]}):(0,a.jsx)(j,{onClick:R,disabled:w,className:"w-full h-12 bg-gradient-to-r from-purple-500 to-pink-500 hover:from-purple-600 hover:to-pink-600 text-white text-lg font-medium rounded-xl shadow-lg",children:w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"启动中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(A.A,{className:"w-5 h-5 mr-2"}),"开始共享桌面"]})})})]}):(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl shadow-lg p-4 sm:p-6 animate-fade-in-up",children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(p.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"观看桌面"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:b?"正在观看桌面共享":"输入连接码观看他人的桌面"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:b?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]}),b&&(0,a.jsx)("div",{className:"mt-1 text-xs text-indigo-600",children:"观看中"})]})]}),(0,a.jsx)("div",{className:"space-y-4",children:b?(0,a.jsxs)("div",{className:"space-y-4",children:[(0,a.jsx)("div",{className:"aspect-video bg-slate-900 rounded-xl flex items-center justify-center text-white",children:(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)(p.A,{className:"w-12 h-12 mx-auto mb-2 opacity-50"}),(0,a.jsx)("p",{className:"text-sm opacity-75",children:"桌面共享画面"})]})}),(0,a.jsxs)(j,{onClick:()=>v(!1),className:"w-full h-12 bg-gradient-to-r from-red-500 to-pink-500 hover:from-red-600 hover:to-pink-600 text-white text-lg font-medium rounded-xl shadow-lg",children:[(0,a.jsx)(D.A,{className:"w-5 h-5 mr-2"}),"断开连接"]})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(N,{value:u,onChange:e=>h(e.target.value.toUpperCase().slice(0,6)),placeholder:"请输入连接码",className:"text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-indigo-500 focus:ring-indigo-500 bg-white/80 backdrop-blur-sm",maxLength:6,disabled:w}),(0,a.jsx)(j,{onClick:W,disabled:6!==u.length||w,className:"w-full h-12 bg-gradient-to-r from-indigo-500 to-purple-500 hover:from-indigo-600 hover:to-purple-600 text-white text-lg font-medium rounded-xl shadow-lg",children:w?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"animate-spin rounded-full h-5 w-5 border-b-2 border-white mr-2"}),"连接中..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(p.A,{className:"w-5 h-5 mr-2"}),"连接桌面"]})})]})})]})]})}var P=t(9509);let L=function(e){let s=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";try{if(void 0!==P&&P.env)return P.env[e]||s}catch(e){}return s},I={isDev:"development"===L("NODE_ENV"),isProd:"production"===L("NODE_ENV"),isStatic:!0,api:{backendUrl:L("GO_BACKEND_URL","http://localhost:8080"),baseUrl:L("NEXT_PUBLIC_API_BASE_URL","http://localhost:3000"),directBackendUrl:L("NEXT_PUBLIC_BACKEND_URL")||window.location.origin,wsUrl:L("NEXT_PUBLIC_WS_URL")||(()=>{{if("localhost"===window.location.hostname&&("3000"===window.location.port||"3001"===window.location.port))return"ws://localhost:8080/ws/p2p";let e="https:"===window.location.protocol?"wss:":"ws:";return"".concat(e,"//").concat(window.location.host,"/ws/p2p")}})()},timeout:{api:3e4,ws:6e4},retry:{max:3,delay:1e3}};var z=t(9803),O=t(227),U=t(9022),M=t(7434),B=t(4416);function _(e){let{selectedFiles:s,fileList:t=[],onFilesChange:r,onGenerateCode:n,pickupCode:o,pickupLink:i,onCopyCode:c,onCopyLink:d,onAddMoreFiles:m,onRemoveFile:x,onClearFiles:u,onReset:g,disabled:p=!1,isConnected:f=!1,isWebSocketConnected:b=!1}=e,[v,N]=(0,l.useState)(!1),w=(0,l.useRef)(null),y=(0,l.useCallback)(e=>{e.preventDefault(),N(!0)},[]),C=(0,l.useCallback)(e=>{e.preventDefault(),e.currentTarget.contains(e.relatedTarget)||N(!1)},[]),k=(0,l.useCallback)(e=>{e.preventDefault(),N(!1);let t=Array.from(e.dataTransfer.files);t.length>0&&r([...s,...t])},[s,r]),R=(0,l.useCallback)(e=>{let t=Array.from(e.target.files||[]);t.length>0&&r([...s,...t])},[s,r]),T=(0,l.useCallback)(e=>{let t=s.filter((s,t)=>t!==e);r(t),x&&x(t)},[s,r,x]),F=(0,l.useCallback)(()=>{w.current&&w.current.click()},[]);return 0!==s.length||o?(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center mb-4 sm:mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(M.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"text-lg font-semibold text-slate-800",children:"已选择文件"}),(0,a.jsxs)("p",{className:"text-sm text-slate-500",children:[s.length," 个文件准备传输"]})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:b?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:f?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):o?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]})]})]}),(0,a.jsx)("div",{className:"space-y-3 mb-4 sm:mb-6",children:s.map((e,s)=>{var l;let r=t.find(s=>s.name===e.name&&s.size===e.size);null==r||r.status;let n=(null==r?void 0:r.progress)||0,o=(null==r?void 0:r.status)||"ready";return(0,a.jsxs)("div",{className:"group bg-gradient-to-r from-slate-50 to-blue-50 border border-slate-200 rounded-xl hover:shadow-md transition-all duration-200",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between p-3 sm:p-4",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 sm:space-x-4 min-w-0 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-lg flex items-center justify-center flex-shrink-0",children:(l=e.type).startsWith("image/")?(0,a.jsx)(S.A,{className:"w-5 h-5 text-white"}):l.startsWith("video/")?(0,a.jsx)(z.A,{className:"w-5 h-5 text-white"}):l.startsWith("audio/")?(0,a.jsx)(O.A,{className:"w-5 h-5 text-white"}):l.includes("zip")||l.includes("rar")?(0,a.jsx)(U.A,{className:"w-5 h-5 text-white"}):(0,a.jsx)(M.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{className:"min-w-0 flex-1",children:[(0,a.jsx)("p",{className:"font-medium text-slate-800 truncate text-sm sm:text-base",children:e.name}),(0,a.jsxs)("div",{className:"flex items-center space-x-2",children:[(0,a.jsx)("p",{className:"text-xs sm:text-sm text-slate-500",children:(e=>{if(0===e)return"0 Bytes";let s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+" "+["Bytes","KB","MB","GB"][s]})(e.size)}),"downloading"===o&&(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-1 h-1 bg-orange-500 rounded-full animate-pulse"}),(0,a.jsx)("span",{className:"text-xs text-orange-600 font-medium",children:"传输中"})]}),"completed"===o&&(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-1 h-1 bg-emerald-500 rounded-full"}),(0,a.jsx)("span",{className:"text-xs text-emerald-600 font-medium",children:"已完成"})]})]})]})]}),(0,a.jsx)(j,{variant:"ghost",size:"sm",onClick:()=>T(s),disabled:p||"downloading"===o,className:"opacity-0 group-hover:opacity-100 text-slate-400 hover:text-red-500 hover:bg-red-50 transition-all duration-200 flex-shrink-0 ml-2 disabled:opacity-50",children:(0,a.jsx)(B.A,{className:"w-4 h-4"})})]}),("downloading"===o||"completed"===o)&&n>0&&(0,a.jsx)("div",{className:"px-3 sm:px-4 pb-3 sm:pb-4",children:(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsxs)("div",{className:"flex justify-between text-xs text-slate-600",children:[(0,a.jsx)("span",{children:"downloading"===o?"正在发送...":"发送完成"}),(0,a.jsxs)("span",{className:"font-medium",children:[n.toFixed(1),"%"]})]}),(0,a.jsx)("div",{className:"w-full bg-slate-200 rounded-full h-2",children:(0,a.jsx)("div",{className:"h-2 rounded-full transition-all duration-300 ".concat("completed"===o?"bg-gradient-to-r from-emerald-500 to-emerald-600":"bg-gradient-to-r from-orange-500 to-orange-600"),style:{width:"".concat(n,"%")}})})]})})]},"".concat(e.name,"-").concat(e.size,"-").concat(s))})}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-3",children:[!o&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)(j,{onClick:n,disabled:p||0===s.length,className:"button-primary text-white px-6 sm:px-8 py-3 rounded-xl font-medium flex-1 min-w-0 shadow-lg",children:[(0,a.jsx)(h.A,{className:"w-5 h-5 mr-2"}),"生成取件码"]}),(0,a.jsx)(j,{onClick:m,variant:"outline",disabled:p,className:"px-6 sm:px-8 py-3 rounded-xl font-medium",children:"添加文件"}),(0,a.jsx)(j,{onClick:g,variant:"outline",disabled:p,className:"text-red-600 hover:bg-red-50 px-6 sm:px-8 py-3 rounded-xl font-medium",children:"重新选择"})]}),o&&(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(j,{variant:"outline",onClick:m,disabled:p,className:"px-6 py-3 rounded-xl border-slate-300 text-slate-600 hover:bg-slate-50 flex-1",children:"添加更多文件"}),s.length>0&&u&&(0,a.jsx)(j,{variant:"outline",onClick:u,disabled:p,className:"px-6 py-3 rounded-xl border-orange-300 text-orange-600 hover:bg-orange-50",children:"清空文件"})]}),(0,a.jsx)(j,{variant:"outline",onClick:g,disabled:p,className:"px-6 py-3 rounded-xl border-red-300 text-red-600 hover:bg-red-50",children:"关闭房间"})]})]}),o&&(0,a.jsxs)("div",{className:"border-t border-slate-200 pt-6",children:[(0,a.jsxs)("div",{className:"text-center mb-4 sm:mb-6",children:[(0,a.jsx)("div",{className:"w-12 h-12 sm:w-16 sm:h-16 mx-auto mb-4 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-2xl flex items-center justify-center animate-float",children:(0,a.jsx)(M.A,{className:"w-6 h-6 sm:w-8 sm:h-8 text-white"})}),(0,a.jsx)("h3",{className:"text-xl sm:text-2xl font-bold bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent mb-2",children:"取件码生成成功!"}),(0,a.jsx)("p",{className:"text-sm sm:text-base text-slate-600",children:"分享以下信息给接收方"})]}),(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"block text-sm font-medium text-slate-700 mb-3",children:"取件码"}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-3",children:[(0,a.jsx)("div",{className:"flex-1 code-display rounded-xl p-4 sm:p-6 text-center",children:(0,a.jsx)("div",{className:"text-2xl sm:text-3xl font-bold font-mono bg-gradient-to-r from-emerald-600 to-teal-600 bg-clip-text text-transparent tracking-wider",children:o})}),(0,a.jsx)(j,{onClick:c,className:"px-4 sm:px-6 py-3 bg-emerald-500 hover:bg-emerald-600 text-white rounded-xl font-medium shadow-lg transition-all duration-200 hover:shadow-xl w-full sm:w-auto",children:"复制"})]})]}),i&&(0,a.jsxs)("div",{children:[(0,a.jsx)("label",{className:"block text-sm font-medium text-slate-700 mb-3",children:"取件链接"}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-3",children:[(0,a.jsx)("div",{className:"flex-1 code-display rounded-xl p-3 sm:p-4",children:(0,a.jsx)("div",{className:"text-xs sm:text-sm text-slate-700 break-all font-mono",children:i})}),(0,a.jsx)(j,{onClick:d,className:"px-4 sm:px-6 py-3 bg-blue-500 hover:bg-blue-600 text-white rounded-xl font-medium shadow-lg transition-all duration-200 hover:shadow-xl w-full sm:w-auto",children:"复制"})]})]})]}),(0,a.jsx)("div",{className:"mt-4 sm:mt-6 p-3 sm:p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200",children:(0,a.jsxs)("p",{className:"text-xs sm:text-sm text-slate-600 text-center",children:["\uD83D\uDCA1 ",(0,a.jsx)("span",{className:"font-medium",children:"使用提示:"}),"接收方输入取件码或访问取件链接即可下载文件"]})})]})]}):(0,a.jsxs)("div",{className:"space-y-6",children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(h.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"选择文件"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:"拖拽文件到下方区域或点击选择文件"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsxs)("div",{className:"flex items-center space-x-1",children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})]})]})]}),(0,a.jsxs)("div",{className:"upload-area rounded-xl p-6 sm:p-8 md:p-12 text-center cursor-pointer ".concat(v?"drag-active":""),onDragOver:y,onDragLeave:C,onDrop:k,onClick:F,children:[(0,a.jsxs)("div",{className:"transition-all duration-300 ".concat(v?"scale-110":""),children:[(0,a.jsx)("div",{className:"w-16 h-16 sm:w-20 sm:h-20 mx-auto mb-4 sm:mb-6 bg-gradient-to-br from-blue-100 to-indigo-100 rounded-full flex items-center justify-center",children:(0,a.jsx)(h.A,{className:"w-8 h-8 sm:w-10 sm:h-10 transition-colors duration-300 ".concat(v?"text-blue-600":"text-slate-400")})}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)("p",{className:"text-lg sm:text-xl font-medium text-slate-700",children:v?"释放文件":"拖拽文件到这里"}),(0,a.jsxs)("p",{className:"text-sm sm:text-base text-slate-500",children:["或者 ",(0,a.jsx)("span",{className:"text-blue-600 font-medium underline",children:"点击选择文件"})]}),(0,a.jsx)("p",{className:"text-xs sm:text-sm text-slate-400 mt-4",children:"支持多个文件同时上传,WebRTC点对点传输"})]})]}),(0,a.jsx)("input",{ref:w,type:"file",multiple:!0,className:"hidden",onChange:R,disabled:p})]})]})}function J(e){let{onJoinRoom:s,files:t,onDownloadFile:r,isConnected:n,isConnecting:o,isWebSocketConnected:i=!1,downloadedFiles:c,error:d=null,onReset:m}=e,[x,u]=(0,l.useState)(""),[h,g]=(0,l.useState)(!1),{showToast:p}=(0,T.d)(),f=async e=>{try{g(!0),console.log("开始验证取件码:",e);let s=await fetch("/api/room-info?code=".concat(e)),t=await s.json();if(console.log("验证响应:",{status:s.status,data:t}),!s.ok||!t.success){let e=t.message||"取件码验证失败";return p(e,"error"),console.log("验证失败:",e),!1}return console.log("取件码验证成功:",t.room),!0}catch(e){return console.error("验证取件码时发生错误:",e),p("网络错误,请检查连接后重试","error"),!1}finally{g(!1)}},b=(0,l.useCallback)(async e=>{if(e.preventDefault(),6===x.length){let e=x.toUpperCase();await f(e)&&s(e)}},[x,s]),v=(0,l.useCallback)(e=>{let s=e.target.value.replace(/[^A-Z0-9]/g,"").toUpperCase();s.length<=6&&u(s)},[]);return(l.useEffect(()=>{if(d&&!o&&!n&&!h){let e=setTimeout(()=>{console.log("重置取件码输入"),u("")},3e3);return()=>clearTimeout(e)}},[d,o,n,h]),(n||o)&&0===t.length)?(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center mb-6",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"等待文件"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:n?"已连接到房间,等待发送方选择文件...":"正在连接到房间..."})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:i?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]})})]})]})]}),(0,a.jsxs)("div",{className:"text-center",children:[(0,a.jsx)("div",{className:"flex items-center justify-center space-x-4 mb-6",children:(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsx)("div",{className:"w-3 h-3 rounded-full mr-2 ".concat(n?"bg-emerald-500 animate-pulse":"bg-orange-500 animate-spin")}),(0,a.jsx)("span",{className:"text-sm font-medium ".concat(n?"text-emerald-600":"text-orange-600"),children:n?"连接已建立":"连接中..."})]})}),(0,a.jsx)("div",{className:"flex justify-center space-x-1 mb-6",children:[void 0,void 0,void 0].map((e,s)=>(0,a.jsx)("div",{className:"w-2 h-2 bg-blue-500 rounded-full animate-bounce",style:{animationDelay:"".concat(.1*s,"s")}},s))}),(0,a.jsx)("div",{className:"p-4 bg-gradient-to-r from-blue-50 to-indigo-50 rounded-xl border border-blue-200",children:(0,a.jsxs)("p",{className:"text-xs sm:text-sm text-slate-600 text-center",children:["\uD83D\uDCA1 ",(0,a.jsx)("span",{className:"font-medium",children:"提示:"}),"房间已连接,发送方清空文件列表后您会看到此界面,等待对方重新选择文件"]})})]})]}):t.length>0?(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{className:"flex items-center",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h3",{className:"text-lg font-semibold text-slate-800",children:"可下载文件"}),(0,a.jsx)("p",{className:"text-sm text-slate-500",children:n?(0,a.jsx)("span",{className:"text-emerald-600",children:"✅ 已连接,可以下载文件"}):(0,a.jsx)("span",{className:"text-amber-600",children:"⏳ 正在建立连接..."})})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:i?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]})})]}),(0,a.jsxs)("div",{className:"mt-1 text-xs text-slate-400",children:[t.length," 个文件"]})]})]}),(0,a.jsx)("div",{children:(0,a.jsx)("div",{className:"space-y-3 sm:space-y-4",children:t.map(e=>{var s;let t="downloading"===e.status,l="completed"===e.status,o=null==c?void 0:c.has(e.id),i=e.progress;return console.log("文件状态:",{fileName:e.name,status:e.status,progress:e.progress,isDownloading:t,currentProgress:i}),(0,a.jsxs)("div",{className:"bg-gradient-to-r from-slate-50 to-blue-50 border border-slate-200 rounded-xl p-3 sm:p-4 hover:shadow-md transition-all duration-200",children:[(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row sm:items-center justify-between mb-3 gap-3",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 sm:space-x-4 flex-1 min-w-0",children:[(0,a.jsx)("div",{className:"w-10 h-10 sm:w-12 sm:h-12 bg-gradient-to-br from-blue-500 to-indigo-500 rounded-lg flex items-center justify-center flex-shrink-0",children:(s=e.type).startsWith("image/")?(0,a.jsx)(S.A,{className:"w-5 h-5 text-white"}):s.startsWith("video/")?(0,a.jsx)(z.A,{className:"w-5 h-5 text-white"}):s.startsWith("audio/")?(0,a.jsx)(O.A,{className:"w-5 h-5 text-white"}):s.includes("zip")||s.includes("rar")?(0,a.jsx)(U.A,{className:"w-5 h-5 text-white"}):(0,a.jsx)(M.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{className:"flex-1 min-w-0",children:[(0,a.jsx)("p",{className:"font-medium text-slate-800 truncate text-sm sm:text-base",children:e.name}),(0,a.jsx)("p",{className:"text-sm text-slate-500",children:(e=>{if(0===e)return"0 Bytes";let s=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,s)).toFixed(2))+" "+["Bytes","KB","MB","GB"][s]})(e.size)}),o&&(0,a.jsx)("p",{className:"text-xs text-emerald-600 font-medium",children:"✅ 传输完成,点击保存"}),t&&(0,a.jsxs)("p",{className:"text-xs text-blue-600 font-medium",children:["⏳ 传输中...",i.toFixed(1),"%"]})]})]}),(0,a.jsxs)(j,{onClick:()=>r(e.id),disabled:!n||t,className:"px-6 py-2 rounded-lg font-medium shadow-lg transition-all duration-200 hover:shadow-xl ".concat(o?"bg-gradient-to-r from-blue-500 to-indigo-500 hover:from-blue-600 hover:to-indigo-600 text-white":t?"bg-slate-300 text-slate-500 cursor-not-allowed":"bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-600 hover:to-teal-600 text-white"),children:[(0,a.jsx)(w.A,{className:"w-4 h-4 mr-2"}),t?"传输中...":o?"保存文件":"开始传输"]})]}),(t||l)&&i>0&&(0,a.jsxs)("div",{className:"mt-3 space-y-2",children:[(0,a.jsxs)("div",{className:"flex justify-between text-sm text-slate-600",children:[(0,a.jsx)("span",{children:o?"传输完成":"正在传输..."}),(0,a.jsxs)("span",{className:"font-medium",children:[i.toFixed(1),"%"]})]}),(0,a.jsx)("div",{className:"w-full bg-slate-200 rounded-full h-2",children:(0,a.jsx)("div",{className:"h-2 rounded-full transition-all duration-300 ".concat(o?"bg-gradient-to-r from-emerald-500 to-emerald-600":"bg-gradient-to-r from-emerald-500 to-teal-500"),style:{width:"".concat(i,"%")}})})]})]},e.id)})})})]}):(0,a.jsxs)("div",{children:[(0,a.jsxs)("div",{className:"flex items-center mb-6 sm:mb-8",children:[(0,a.jsxs)("div",{className:"flex items-center space-x-3 flex-1",children:[(0,a.jsx)("div",{className:"w-10 h-10 bg-gradient-to-br from-emerald-500 to-teal-500 rounded-xl flex items-center justify-center",children:(0,a.jsx)(w.A,{className:"w-5 h-5 text-white"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-800",children:"输入取件码"}),(0,a.jsx)("p",{className:"text-sm text-slate-600",children:"请输入6位取件码来获取文件"})]})]}),(0,a.jsx)("div",{className:"w-px h-12 bg-slate-200 mx-4"}),(0,a.jsxs)("div",{className:"text-right",children:[(0,a.jsx)("div",{className:"text-sm text-slate-500 mb-1",children:"连接状态"}),(0,a.jsxs)("div",{className:"flex items-center justify-end space-x-3 text-sm",children:[(0,a.jsx)("div",{className:"flex items-center space-x-1",children:o?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"WS"})]}):i?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"WS"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"WS"})]})}),(0,a.jsx)("div",{className:"text-slate-300",children:"|"}),(0,a.jsx)("div",{className:"flex items-center space-x-1",children:n?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-emerald-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-emerald-600",children:"RTC"})]}):o?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-orange-500 animate-pulse"}),(0,a.jsx)("span",{className:"text-orange-600",children:"RTC"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-2 h-2 rounded-full bg-slate-400"}),(0,a.jsx)("span",{className:"text-slate-600",children:"RTC"})]})})]})]})]}),(0,a.jsxs)("form",{onSubmit:b,className:"space-y-4 sm:space-y-6",children:[(0,a.jsxs)("div",{className:"space-y-3",children:[(0,a.jsxs)("div",{className:"relative",children:[(0,a.jsx)(N,{value:x,onChange:v,placeholder:"请输入取件码",className:"text-center text-2xl sm:text-3xl tracking-[0.3em] sm:tracking-[0.5em] font-mono h-12 sm:h-16 border-2 border-slate-200 rounded-xl focus:border-emerald-500 focus:ring-emerald-500 bg-white/80 backdrop-blur-sm pb-2 sm:pb-4",maxLength:6,disabled:h||o}),(0,a.jsx)("div",{className:"absolute inset-x-0 -bottom-4 sm:-bottom-6 flex justify-center space-x-1 sm:space-x-2",children:[...Array(6)].map((e,s)=>(0,a.jsx)("div",{className:"w-1.5 h-1.5 sm:w-2 sm:h-2 rounded-full transition-all duration-200 ".concat(s{let e=(0,r.useSearchParams)(),s=(0,r.useRouter)(),{showToast:t}=(0,T.d)(),[n,o]=(0,l.useState)([]),[i,c]=(0,l.useState)([]),[d,m]=(0,l.useState)(new Map),[x,u]=(0,l.useState)(null),[g,p]=(0,l.useState)(""),[f,b]=(0,l.useState)("send"),[v,N]=(0,l.useState)(!1),y=(0,l.useRef)(null),{isConnected:C,isConnecting:k,isWebSocketConnected:S,error:R,connect:F,disconnect:E,sendFile:A,sendFileList:D,requestFile:W,onFileReceived:P,onFileListReceived:L,onFileRequested:z,onFileProgress:O}=function(){let e=function(){let[e,s]=(0,l.useState)({isConnected:!1,isConnecting:!1,isWebSocketConnected:!1,error:null,localDataChannel:null,remoteDataChannel:null}),t=(0,l.useCallback)(()=>{var e;let s=navigator.userAgent,t=/Edg/.test(s),a=/Chrome/.test(s)&&!t,l=/Safari/.test(s)&&!a&&!t,r=/Firefox/.test(s),n=a||t;return console.log("浏览器检测结果:",{userAgent:s.substring(0,100)+"...",isEdge:t,isChrome:a,isSafari:l,isFirefox:r,isChromeFamily:n,webRTCSupport:{RTCPeerConnection:!!window.RTCPeerConnection,getUserMedia:!!(null==(e=navigator.mediaDevices)?void 0:e.getUserMedia),WebSocket:!!window.WebSocket}}),{isEdge:t,isChrome:a,isSafari:l,isFirefox:r,isChromeFamily:n}},[]),a=(0,l.useRef)(null),r=(0,l.useRef)(null),n=(0,l.useRef)(null),o=(0,l.useRef)(null),i=(0,l.useRef)([]),c=(0,l.useRef)(null),d=[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"},{urls:"stun:stun3.l.google.com:19302"},{urls:"stun:stun4.l.google.com:19302"},{urls:"stun:stun.nextcloud.com:443"},{urls:"stun:stun.sipgate.net:10000"},{urls:"stun:stun.ekiga.net"}],m=(0,l.useCallback)(()=>{let{isEdge:e,isSafari:s,isChromeFamily:a}=t(),l={iceServers:d,iceCandidatePoolSize:10,bundlePolicy:"max-bundle",rtcpMuxPolicy:"require",iceTransportPolicy:"all"};return a?(console.log("应用 Chrome 内核浏览器优化配置"),{...l,iceCandidatePoolSize:20,bundlePolicy:"max-bundle",rtcpMuxPolicy:"require",iceTransportPolicy:"all",sdpSemantics:"unified-plan"}):s?(console.log("应用 Safari 浏览器优化配置"),{...l,iceCandidatePoolSize:8,sdpSemantics:"unified-plan"}):(console.log("应用默认浏览器配置"),l)},[t,d]),x=(0,l.useCallback)(e=>{s(s=>({...s,...e}))},[]),u=(0,l.useCallback)(async()=>{let e=r.current;if(e&&e.remoteDescription&&0!==i.current.length){for(let s of(console.log("处理缓存的ICE候选,数量:",i.current.length),i.current))try{await e.addIceCandidate(s),console.log("已添加缓存的ICE候选")}catch(e){console.warn("添加缓存ICE候选失败:",e)}i.current=[]}},[]),h=(0,l.useCallback)(async(e,s)=>{console.log("开始创建offer...");let a=t();try{let t=a.isChromeFamily?{offerToReceiveAudio:!1,offerToReceiveVideo:!1,iceRestart:!1,voiceActivityDetection:!1}:{offerToReceiveAudio:!1,offerToReceiveVideo:!1,iceRestart:!1};console.log("使用的 offer 配置:",t);let l=await e.createOffer(t);await e.setLocalDescription(l),console.log("已设置本地描述,等待ICE候选收集...");let r=a.isChromeFamily?5e3:3e3;c.current=setTimeout(()=>{console.log("ICE收集超时,发送当前offer"),s.readyState===WebSocket.OPEN&&(s.send(JSON.stringify({type:"offer",payload:e.localDescription})),console.log("已发送offer (超时发送)"))},r);let n=()=>{if(c.current&&(clearTimeout(c.current),c.current=null),s.readyState===WebSocket.OPEN){var t;s.send(JSON.stringify({type:"offer",payload:e.localDescription}));let a=(null==(t=e.localDescription)?void 0:t.sdp)?e.localDescription.sdp.split("a=candidate:").length-1:0;console.log("已发送offer (ICE收集完成)","候选数量:",a)}};if("complete"===e.iceGatheringState)n();else{let s=e.onicegatheringstatechange;e.onicegatheringstatechange=t=>{console.log("ICE收集状态变化:",e.iceGatheringState),s&&s.call(e,t),"complete"===e.iceGatheringState&&(n(),e.onicegatheringstatechange=s)}}}catch(e){console.error("创建offer失败:",e),x({error:"创建连接失败",isConnecting:!1})}},[x,t]),g=(0,l.useCallback)(()=>{o.current&&(clearTimeout(o.current),o.current=null)},[]),p=(0,l.useCallback)(()=>{var e;console.warn("WebRTC连接超时");let s=r.current,t={connectionState:(null==s?void 0:s.connectionState)||"unknown",iceConnectionState:(null==s?void 0:s.iceConnectionState)||"unknown",signalingState:(null==s?void 0:s.signalingState)||"unknown",isWebSocketConnected:(null==(e=a.current)?void 0:e.readyState)===WebSocket.OPEN};console.log("连接超时时的状态:",t),x({error:"连接超时 - WebSocket: ".concat(t.isWebSocketConnected?"已连接":"未连接",", 信令状态: ").concat(t.signalingState,", 连接状态: ").concat(t.connectionState),isConnecting:!1}),a.current&&a.current.close(),r.current&&r.current.close()},[x]),f=(0,l.useCallback)(async(s,l)=>{console.log("=== 开始WebRTC连接 ==="),console.log("房间代码:",s,"角色:",l);let c=t();console.log("当前浏览器:",c),g(),x({isConnecting:!0,error:null});let d=c.isChromeFamily?6e4:3e4;"receiver"===l&&(o.current=setTimeout(()=>{p()},d));try{let t=m();console.log("使用的 RTCConfiguration:",t);let o=new RTCPeerConnection(t);r.current=o;let d=I.api.wsUrl.replace("/ws/p2p","/ws/webrtc"),p="".concat(d,"?code=").concat(s,"&role=").concat(l);console.log("WebSocket 连接信息:",{原始wsUrl:I.api.wsUrl,替换后wsUrl:d,最终URL:p,当前域名:window.location.host,协议:window.location.protocol,端口:window.location.port,浏览器:c});let f=new WebSocket(p);a.current=f;let b=null;if(c.isChromeFamily&&(b=setTimeout(()=>{f.readyState===WebSocket.CONNECTING&&(console.error("Chrome内核 - WebSocket连接超时"),f.close(),x({error:"WebSocket连接超时 - Chrome内核可能存在安全策略限制",isConnecting:!1}))},1e4)),f.onopen=async()=>{console.log("WebSocket连接已建立,URL:",p),b&&(clearTimeout(b),b=null),x({isWebSocketConnected:!0}),"sender"===l&&(h(o,f),x({isConnecting:!1}))},f.onmessage=async e=>{try{var s,t;let a=JSON.parse(e.data);switch(console.log("收到信令消息:",a,"当前PC状态:",o.signalingState),a.type){case"offer":if(a.payload){console.log("处理offer,当前状态:",o.signalingState);try{if("stable"!==o.signalingState)return void console.warn("跳过offer,信令状态不为stable:",o.signalingState);await o.setRemoteDescription(new RTCSessionDescription(a.payload)),console.log("已设置远程描述,状态变为:",o.signalingState);let e=await o.createAnswer();await o.setLocalDescription(e),console.log("已创建并设置本地answer,状态变为:",o.signalingState),f.readyState===WebSocket.OPEN&&(f.send(JSON.stringify({type:"answer",payload:e})),console.log("已发送answer")),x({isConnecting:!1}),await u()}catch(e){console.error("处理offer失败:",e),x({error:"信令交换失败",isConnecting:!1})}}break;case"answer":if(a.payload){console.log("处理answer,当前状态:",o.signalingState);try{if("have-local-offer"!==o.signalingState)return void console.warn("跳过answer,信令状态不为have-local-offer:",o.signalingState);await o.setRemoteDescription(new RTCSessionDescription(a.payload)),console.log("信令交换完成,状态变为:",o.signalingState),await u()}catch(e){console.error("处理answer失败:",e),x({error:"信令交换失败",isConnecting:!1})}}break;case"ice-candidate":if(a.payload)try{let e=new RTCIceCandidate(a.payload);if("closed"===o.signalingState)return void console.warn("跳过ICE候选,连接已关闭");o.remoteDescription?(await o.addIceCandidate(e),console.log("已添加ICE候选:",{type:e.type,protocol:e.protocol,address:(null==(s=e.address)?void 0:s.substring(0,10))+"...",port:e.port})):(i.current.push(e),console.log("缓存ICE候选,等待远程描述:",{type:e.type,缓存数量:i.current.length}))}catch(e){console.warn("处理ICE候选失败:",e)}break;case"disconnection":console.log("收到断开连接通知:",a.payload);let l=(null==(t=a.payload)?void 0:t.message)||"对方已停止传输";x({error:l,isConnecting:!1,isConnected:!1,isWebSocketConnected:!1}),f.readyState===WebSocket.OPEN&&f.close(1e3,"对方已断开连接"),"closed"!==o.connectionState&&o.close();break;case"error":console.error("信令错误:",a.error),x({error:a.error,isConnecting:!1})}}catch(e){console.error("处理信令消息失败:",e),x({error:"信令处理失败",isConnecting:!1})}},f.onerror=e=>{console.error("WebSocket错误:",e),console.error("WebSocket连接失败,URL:",p),b&&(clearTimeout(b),b=null);let s=c.isChromeFamily?"WebSocket连接失败 - Chrome内核可能阻止了不安全的连接,请确保使用HTTPS":"WebSocket连接失败";x({error:s,isConnecting:!1,isWebSocketConnected:!1})},f.onclose=e=>{console.log("WebSocket连接已关闭,代码:",e.code,"原因:",e.reason,"URL:",p),x({isWebSocketConnected:!1})},o.onicecandidate=e=>{if(e.candidate){let s={type:e.candidate.type,protocol:e.candidate.protocol,address:e.candidate.address,port:e.candidate.port,priority:e.candidate.priority,foundation:e.candidate.foundation};if(console.log("ICE候选信息:",s),c.isChromeFamily&&null!==e.candidate.priority&&"host"===e.candidate.type&&e.candidate.priority<1e6){console.log("Chrome内核 - 延迟发送低质量候选"),setTimeout(()=>{f.readyState===WebSocket.OPEN&&f.send(JSON.stringify({type:"ice-candidate",payload:e.candidate}))},800);return}f.readyState===WebSocket.OPEN&&f.send(JSON.stringify({type:"ice-candidate",payload:e.candidate}))}else console.log("ICE候选收集完成")},o.onicegatheringstatechange=()=>{console.log("ICE收集状态变化:",o.iceGatheringState)},o.onsignalingstatechange=()=>{switch(console.log("信令状态变化:",o.signalingState),o.signalingState){case"stable":console.log("信令协商完成,连接稳定");break;case"have-local-offer":console.log("已发送offer,等待answer");break;case"have-remote-offer":console.log("已接收offer,需要发送answer");break;case"have-local-pranswer":console.log("已发送provisional answer");break;case"have-remote-pranswer":console.log("已接收provisional answer");break;case"closed":console.log("连接已关闭");break;default:console.warn("未知的信令状态:",o.signalingState)}},o.onconnectionstatechange=()=>{switch(console.log("连接状态变化:",o.connectionState),o.connectionState){case"new":console.log("连接初始化");break;case"connecting":console.log("正在建立连接..."),e.isConnected||x({isConnecting:!0});break;case"connected":console.log("连接已建立"),g(),x({isConnected:!0,isConnecting:!1});break;case"disconnected":console.log("连接已断开"),x({isConnected:!1});break;case"failed":console.log("连接失败"),g(),x({error:"连接失败",isConnecting:!1,isConnected:!1});break;case"closed":console.log("连接已关闭"),x({isConnected:!1,isConnecting:!1})}},o.oniceconnectionstatechange=()=>{switch(console.log("ICE连接状态变化:",o.iceConnectionState),o.iceConnectionState){case"new":console.log("ICE连接初始化");break;case"checking":console.log("ICE正在检查连通性...");break;case"connected":console.log("ICE连接成功"),g();break;case"completed":console.log("ICE连接完成"),g();break;case"disconnected":console.log("ICE连接断开"),x({error:"ICE连接断开",isConnected:!1});break;case"failed":console.log("ICE连接失败"),g(),x({error:"ICE连接失败",isConnecting:!1,isConnected:!1});break;case"closed":console.log("ICE连接已关闭")}},"sender"===l){let e=c.isChromeFamily?{ordered:!0,maxPacketLifeTime:void 0,maxRetransmits:void 0,negotiated:!1,id:void 0,protocol:""}:{ordered:!0,maxPacketLifeTime:void 0,maxRetransmits:void 0};console.log("创建数据通道,配置:",e);let s=o.createDataChannel("fileTransfer",e);n.current=s,s.bufferedAmountLowThreshold=262144,s.onopen=()=>{console.log("数据通道已打开 (发送方)"),g(),console.log("数据通道配置:",{id:s.id,label:s.label,maxPacketLifeTime:s.maxPacketLifeTime,maxRetransmits:s.maxRetransmits,ordered:s.ordered,bufferedAmountLowThreshold:s.bufferedAmountLowThreshold,readyState:s.readyState}),x({localDataChannel:s})},s.onclose=()=>{console.log("数据通道已关闭 (发送方)"),x({localDataChannel:null})},s.onerror=e=>{console.error("数据通道错误 (发送方):",e),x({error:"数据通道连接失败",isConnecting:!1})}}else o.ondatachannel=e=>{let s=e.channel;n.current=s,console.log("收到数据通道 (接收方),标签:",s.label),s.onopen=()=>{console.log("数据通道已打开 (接收方)"),g(),console.log("数据通道配置:",{id:s.id,label:s.label,readyState:s.readyState}),x({remoteDataChannel:s})},s.onclose=()=>{console.log("数据通道已关闭 (接收方)"),x({remoteDataChannel:null})},s.onerror=e=>{console.error("数据通道错误 (接收方):",e),x({error:"数据通道连接失败",isConnecting:!1})}}}catch(e){console.error("连接失败:",e),g(),x({error:e instanceof Error?e.message:"连接失败",isConnecting:!1})}},[x,g,p,u,h,t,m]),b=(0,l.useCallback)(()=>{console.log("断开WebRTC连接"),g(),c.current&&(clearTimeout(c.current),c.current=null,console.log("已清理ICE收集超时定时器")),i.current=[],n.current&&(n.current.close(),n.current=null),r.current&&(r.current.close(),r.current=null),a.current&&(a.current.close(),a.current=null),s({isConnected:!1,isConnecting:!1,isWebSocketConnected:!1,error:null,localDataChannel:null,remoteDataChannel:null})},[g]),v=(0,l.useCallback)(()=>n.current,[]);return{...e,connect:f,disconnect:b,getDataChannel:v}}(),s=function(){let[e,s]=(0,l.useState)({isTransferring:!1,transferProgress:0,receivedFiles:[],error:null}),t=(0,l.useRef)(new Map),a=(0,l.useRef)(null),r=(0,l.useRef)([]),n=(0,l.useRef)([]),o=(0,l.useRef)([]),i=(0,l.useCallback)(e=>{s(s=>({...s,...e}))},[]),c=(0,l.useCallback)(async(e,s,t)=>{if(!t||"open"!==t.readyState){console.error("数据通道未准备就绪"),i({error:"数据通道未准备就绪"});return}console.log("=== 开始发送文件 ==="),console.log("文件:",e.name,"大小:",e.size,"ID:",s),i({isTransferring:!0,transferProgress:0,error:null});try{let a={id:s,name:e.name,size:e.size,type:e.type},l=JSON.stringify({type:"file-start",payload:a});console.log("发送文件元数据:",l),t.send(l);let r=Math.ceil(e.size/262144);console.log("总分块数:",r);let n=0,c=()=>{if(n>=r){let a=JSON.stringify({type:"file-end",payload:{id:s}});t.send(a),i({isTransferring:!1,transferProgress:100}),console.log("文件发送完成:",e.name);return}let a=262144*n,l=Math.min(a+262144,e.size),d=e.slice(a,l),m=new FileReader;m.onload=a=>{var l;if((null==(l=a.target)?void 0:l.result)&&"open"===t.readyState){let l=a.target.result;if(t.bufferedAmount>1048576){console.log("数据通道缓冲区满,等待清空...");let e=()=>{t.bufferedAmount<262144?d():setTimeout(e,10)};e()}else d();function d(){let a=JSON.stringify({type:"file-chunk",payload:{fileId:s,chunkIndex:n,totalChunks:r}});t.send(a),t.send(l);let d=++n/r*100;i({transferProgress:d}),o.current.forEach(t=>{t({fileId:s,fileName:e.name,progress:d})}),console.log("发送进度: ".concat(d.toFixed(1),"%, 块: ").concat(n,"/").concat(r,", 文件: ").concat(e.name,", 缓冲区: ").concat(t.bufferedAmount," bytes")),c()}}},m.onerror=()=>{console.error("读取文件块失败"),i({error:"读取文件失败",isTransferring:!1})},m.readAsArrayBuffer(d)};c()}catch(e){console.error("发送文件失败:",e),i({error:e instanceof Error?e.message:"发送文件失败",isTransferring:!1})}},[i]),d=(0,l.useCallback)(e=>{if("string"==typeof e.data)try{let l=JSON.parse(e.data);switch(console.log("收到消息:",l.type,l.payload),l.type){case"file-list":console.log("文件列表消息将由主hook处理");return;case"file-start":let o=l.payload;console.log("开始接收文件:",o.name,"大小:",o.size),t.current.set(o.id,{metadata:o,chunks:[],receivedChunks:0,totalChunks:Math.ceil(o.size/262144)}),i({isTransferring:!0,transferProgress:0});break;case"file-chunk":let{fileId:c,chunkIndex:d,totalChunks:m}=l.payload;console.log("接收文件块信息: ".concat(d+1,"/").concat(m,", 文件ID: ").concat(c)),a.current={fileId:c,chunkIndex:d,totalChunks:m};break;case"file-end":let{id:x}=l.payload,u=t.current.get(x);if(u){let e=new Blob(u.chunks,{type:u.metadata.type}),a=new File([e],u.metadata.name,{type:u.metadata.type});console.log("文件接收完成:",a.name),s(e=>({...e,receivedFiles:[...e.receivedFiles,{id:x,file:a}],isTransferring:!1,transferProgress:100})),n.current.forEach(e=>{e({id:x,file:a})}),t.current.delete(x)}break;case"file-request":let{fileId:h,fileName:g}=l.payload;console.log("收到文件请求:",g,"ID:",h),r.current.forEach(e=>{e(h,g)})}}catch(e){console.error("解析消息失败:",e)}else if(e.data instanceof ArrayBuffer){let s=e.data;if(console.log("收到文件块数据:",s.byteLength,"bytes"),a.current){let{fileId:e,chunkIndex:l}=a.current,r=t.current.get(e);if(r){if(!r.chunks[l]){r.chunks[l]=s,r.receivedChunks++;let t=r.receivedChunks/r.totalChunks*100;i({transferProgress:t}),o.current.forEach(s=>{s({fileId:e,fileName:r.metadata.name,progress:t})}),console.log("文件接收进度: ".concat(t.toFixed(1),"%, 块: ").concat(r.receivedChunks,"/").concat(r.totalChunks,", 文件: ").concat(r.metadata.name))}a.current=null}}else console.warn("收到块数据但没有对应的块信息")}},[i]),m=(0,l.useCallback)((e,s,t)=>{if(!t||"open"!==t.readyState)return void console.error("数据通道未准备就绪");console.log("请求文件:",s,"ID:",e);let a=JSON.stringify({type:"file-request",payload:{fileId:e,fileName:s}});t.send(a)},[]),x=(0,l.useCallback)(e=>(r.current.push(e),()=>{let s=r.current.indexOf(e);s>-1&&r.current.splice(s,1)}),[]),u=(0,l.useCallback)(e=>(n.current.push(e),()=>{let s=n.current.indexOf(e);s>-1&&n.current.splice(s,1)}),[]),h=(0,l.useCallback)(e=>(o.current.push(e),()=>{let s=o.current.indexOf(e);s>-1&&o.current.splice(s,1)}),[]);return{...e,sendFile:c,requestFile:m,handleMessage:d,onFileRequested:x,onFileReceived:u,onFileProgress:h}}(),t=(0,l.useRef)([]);(0,l.useEffect)(()=>{let a=e.localDataChannel||e.remoteDataChannel;if(a&&"open"===a.readyState){console.log("设置数据通道消息处理器, 通道类型:",e.localDataChannel?"本地":"远程");let l=s.handleMessage;a.onmessage=e=>{if(console.log("收到数据通道消息:",typeof e.data),"string"==typeof e.data)try{let s=JSON.parse(e.data);if("file-list"===s.type){console.log("收到文件列表:",s.payload),t.current.forEach(e=>{e(s.payload)});return}}catch(e){console.error("解析文件列表消息失败:",e)}l(e)}}},[e.localDataChannel,e.remoteDataChannel,s.handleMessage]);let a=(0,l.useCallback)((t,a)=>{let l=e.getDataChannel();if(!l)return void console.error("数据通道未准备就绪");let r=a||"file_".concat(Date.now());console.log("=== 发送文件 ==="),console.log("文件:",t.name,"ID:",r,"大小:",t.size),s.sendFile(t,r,l)},[e.getDataChannel,s.sendFile]),r=(0,l.useCallback)((t,a)=>{let l=e.getDataChannel();if(!l)return void console.error("数据通道未准备就绪");console.log("=== 请求文件 ==="),console.log("文件:",a,"ID:",t),s.requestFile(t,a,l)},[e.getDataChannel,s.requestFile]),n=(0,l.useCallback)(s=>{let t=e.getDataChannel();if(!t||"open"!==t.readyState)return void console.error("数据通道未准备就绪,无法发送文件列表");console.log("=== 发送文件列表 ==="),console.log("文件列表:",s);let a=JSON.stringify({type:"file-list",payload:s});try{t.send(a),console.log("文件列表已发送")}catch(e){console.error("发送文件列表失败:",e)}},[e.getDataChannel]),o=(0,l.useCallback)(e=>(console.log("注册文件列表回调"),t.current.push(e),()=>{let s=t.current.indexOf(e);s>-1&&t.current.splice(s,1)}),[]);return{isConnected:e.isConnected,isConnecting:e.isConnecting,isWebSocketConnected:e.isWebSocketConnected,error:e.error||s.error,isTransferring:s.isTransferring,transferProgress:s.transferProgress,receivedFiles:s.receivedFiles,connect:e.connect,disconnect:e.disconnect,sendFile:a,requestFile:r,sendFileList:n,onFileRequested:s.onFileRequested,onFileReceived:s.onFileReceived,onFileProgress:s.onFileProgress,onFileListReceived:o}}();(0,l.useEffect)(()=>{let s=e.get("mode"),t=e.get("type"),a=e.get("code");!v&&"webrtc"===t&&s&&["send","receive"].includes(s)&&(console.log("=== 处理初始URL参数 ==="),console.log("URL模式:",s,"类型:",t,"取件码:",a),b(s),N(!0),a&&"receive"===s&&(console.log("URL中有取件码,自动加入房间"),q(a)))},[e,v]);let U=(0,l.useCallback)(t=>{console.log("=== 手动切换模式 ==="),console.log("新模式:",t),b(t);let a=new URLSearchParams(e.toString());a.set("type","webrtc"),a.set("mode",t),"send"===t&&a.delete("code"),s.push("?".concat(a.toString()),{scroll:!1})},[e,s]),M=()=>Date.now().toString(36)+Math.random().toString(36).substr(2),B=async()=>{if(0===n.length)return void t("需要选择文件才能创建传输房间","error");try{console.log("=== 创建房间 ==="),console.log("选中文件数:",n.length);let e=await fetch("/api/create-room",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({files:n.map(e=>({name:e.name,size:e.size,type:e.type,lastModified:e.lastModified}))})}),s=await e.json();if(!e.ok)throw Error(s.error||"创建房间失败");let a=s.code;p(a),console.log("房间创建成功,取件码:",a),F(a,"sender"),t("房间创建成功,取件码: ".concat(a),"success")}catch(e){console.error("创建房间失败:",e),t(e instanceof Error?e.message:"网络错误,请重试","error")}},q=async e=>{console.log("=== 加入房间 ==="),console.log("取件码:",e);let s=e.trim();if(!s||6!==s.length)return void t("请输入正确的6位取件码","error");try{console.log("检查房间状态..."),t("正在检查房间状态...","info");let e=await fetch("/api/webrtc-room-status?code=".concat(s)),a=await e.json();if(!a.success)return void t(a.message||"房间不存在或已过期","error");if(!a.sender_online)return void t("发送方不在线,请确认取件码是否正确","error");console.log("房间状态检查通过,开始连接..."),p(s),F(s,"receiver"),t("正在连接到房间: ".concat(s),"info")}catch(e){console.error("检查房间状态失败:",e),t("检查房间状态失败,请重试","error")}};(0,l.useEffect)(()=>L(e=>{console.log("=== 收到文件列表更新 ==="),console.log("文件列表:",e),console.log("当前模式:",f),"receive"===f&&c(e)}),[L,f]),(0,l.useEffect)(()=>{R&&"receive"===f&&(console.log("=== 连接错误处理 ==="),console.log("错误信息:",R),t("连接失败: ".concat(R),"error"))},[R,f,t]),(0,l.useEffect)(()=>P(e=>{console.log("=== 接收到文件 ==="),console.log("文件:",e.file.name,"ID:",e.id),m(s=>new Map(s.set(e.id,e.file))),c(s=>s.map(s=>s.id===e.id?{...s,status:"completed",progress:100}:s)),t("".concat(e.file.name," 已准备好下载"),"success")}),[P,t]),(0,l.useEffect)(()=>O(e=>{console.log("=== 文件进度更新 ==="),console.log("文件:",e.fileName,"ID:",e.fileId,"进度:",e.progress),u({fileId:e.fileId,fileName:e.fileName,progress:e.progress}),c(s=>s.map(s=>{if(s.id===e.fileId||s.name===e.fileName){let t=e.progress,a=t>=100?"completed":"downloading";return console.log("更新文件 ".concat(s.name," 进度: ").concat(s.progress," -> ").concat(t)),{...s,progress:t,status:a}}return s})),e.progress>=100&&"send"===f&&(t("文件发送完成: ".concat(e.fileName),"success"),u(null))}),[O,f,t]),(0,l.useEffect)(()=>z((e,s)=>{if(console.log("=== 收到文件请求 ==="),console.log("文件:",s,"ID:",e,"当前模式:",f),"send"===f){console.log("当前选中的文件列表:",n.map(e=>e.name));let a=n.find(e=>e.name===s);if(!a){console.error("找不到匹配的文件:",s),console.log("可用文件:",n.map(e=>"".concat(e.name," (").concat(e.size," bytes)"))),t("无法找到文件: ".concat(s),"error");return}console.log("找到匹配文件,开始发送:",a.name,"ID:",e,"文件大小:",a.size),c(t=>t.map(t=>t.id===e||t.name===s?{...t,status:"downloading",progress:0}:t)),A(a,e),t("开始发送文件: ".concat(s),"info")}else console.warn("接收模式下收到文件请求,忽略")}),[z,f,n,A,t]),(0,l.useEffect)(()=>{if(console.log("=== 连接状态变化 ==="),console.log("连接状态:",{isConnected:C,pickupCode:g,mode:f,selectedFilesCount:n.length,fileListCount:i.length}),C&&g&&"send"===f&&n.length>0)if(0===i.length){console.log("创建文件列表并发送...");let e=n.map(e=>({id:M(),name:e.name,size:e.size,type:e.type,status:"ready",progress:0}));c(e),setTimeout(()=>{D(e)},500)}else i.length>0&&(console.log("发送现有文件列表..."),setTimeout(()=>{D(i)},500))},[C,g,f,n.length]);let G=g?"".concat(window.location.origin,"?type=webrtc&mode=receive&code=").concat(g):"";return(0,l.useEffect)(()=>{R&&t(R,"error")},[R,t]),(0,a.jsxs)("div",{className:"space-y-4 sm:space-y-6",children:[(0,a.jsx)("div",{className:"flex justify-center mb-6",children:(0,a.jsxs)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-1 shadow-lg",children:[(0,a.jsxs)(j,{variant:"send"===f?"default":"ghost",onClick:()=>U("send"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(h.A,{className:"w-4 h-4 mr-2"}),"发送文件"]}),(0,a.jsxs)(j,{variant:"receive"===f?"default":"ghost",onClick:()=>U("receive"),className:"px-6 py-2 rounded-lg",children:[(0,a.jsx)(w.A,{className:"w-4 h-4 mr-2"}),"接收文件"]})]})}),"send"===f?(0,a.jsx)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-4 sm:p-6 shadow-lg border border-white/20 animate-fade-in-up",children:(0,a.jsx)(_,{selectedFiles:n,fileList:i,onFilesChange:o,onGenerateCode:B,pickupCode:g,pickupLink:G,onCopyCode:()=>{navigator.clipboard.writeText(g),t("取件码已复制到剪贴板","success")},onCopyLink:()=>{let e="".concat(window.location.origin,"?type=webrtc&mode=receive&code=").concat(g);navigator.clipboard.writeText(e),t("取件链接已复制到剪贴板","success")},onAddMoreFiles:()=>{var e;null==(e=y.current)||e.click()},onRemoveFile:o,onClearFiles:()=>{console.log("=== 清空文件 ==="),o([]),c([]),C&&g&&D([])},onReset:()=>{console.log("=== 重置房间 ==="),E(),p(""),o([]),c([]),m(new Map)},disabled:!!x,isConnected:C,isWebSocketConnected:S})}):(0,a.jsx)("div",{className:"bg-white/80 backdrop-blur-sm rounded-xl p-4 sm:p-6 shadow-lg border border-white/20 animate-fade-in-up",children:(0,a.jsx)(J,{onJoinRoom:q,files:i,onDownloadFile:e=>{d.get(e)?(e=>{let s=d.get(e);if(!s)return;let a=URL.createObjectURL(s),l=document.createElement("a");l.href=a,l.download=s.name,document.body.appendChild(l),l.click(),document.body.removeChild(l),URL.revokeObjectURL(a),t("".concat(s.name," 已保存到下载文件夹"),"success")})(e):(e=>{if("receive"!==f)return console.error("requestFile只能在接收模式下调用");let s=i.find(s=>s.id===e);if(!s)return console.error("找不到文件信息:",e);console.log("=== 开始请求文件 ==="),console.log("文件信息:",{name:s.name,id:e,size:s.size}),console.log("当前文件状态:",s.status),console.log("WebRTC连接状态:",{isConnected:C,isTransferring:!!x}),c(s=>{let t=s.map(s=>s.id===e?{...s,status:"downloading",progress:0}:s);return console.log("更新后的文件列表:",t.find(s=>s.id===e)),t}),console.log("调用hook的requestFile..."),W(e,s.name),t("正在请求文件: ".concat(s.name),"info")})(e)},isConnected:C,isConnecting:k,isWebSocketConnected:S,downloadedFiles:d,error:R,onReset:()=>{if(console.log("=== 重置连接状态 ==="),E(),p(""),c([]),m(new Map),"receive"===f){let t=new URLSearchParams(e.toString());t.delete("code"),s.push("?".concat(t.toString()),{scroll:!1})}}})}),(0,a.jsx)("input",{ref:y,type:"file",multiple:!0,onChange:e=>(e=>{console.log("=== 文件选择 ==="),console.log("新文件:",e.map(e=>e.name)),o(s=>[...s,...e]);let s=e.map(e=>({id:M(),name:e.name,size:e.size,type:e.type,status:"ready",progress:0}));c(e=>{let t=[...e,...s];return console.log("更新后的文件列表:",t),C&&g&&(console.log("立即同步文件列表到对端"),setTimeout(()=>D(t),100)),t})})(Array.from(e.target.files||[])),className:"hidden"})]})};function G(){let e=(0,r.useSearchParams)(),[s,t]=(0,l.useState)("webrtc"),[n,o]=(0,l.useState)(!1);return(0,l.useEffect)(()=>{if(!n){let s=e.get("type");s&&["webrtc","text","desktop"].includes(s)&&t(s),o(!0)}},[e,n]),(0,a.jsx)("div",{className:"min-h-screen bg-gradient-to-br from-slate-50 to-blue-50",children:(0,a.jsxs)("div",{className:"container mx-auto px-4 py-4 sm:py-6 md:py-8",children:[(0,a.jsx)("div",{className:"text-center mb-6 sm:mb-8",children:(0,a.jsx)(f,{})}),(0,a.jsx)("div",{className:"max-w-4xl mx-auto",children:(0,a.jsxs)(d,{value:s,onValueChange:t,className:"w-full",children:[(0,a.jsx)("div",{className:"mb-6",children:(0,a.jsxs)(m,{className:"grid w-full grid-cols-3 max-w-lg mx-auto h-auto bg-white/90 backdrop-blur-sm shadow-lg rounded-xl p-2 border border-slate-200",children:[(0,a.jsxs)(x,{value:"webrtc",className:"flex items-center justify-center space-x-2 px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 hover:bg-slate-50 data-[state=active]:bg-blue-500 data-[state=active]:text-white data-[state=active]:shadow-md data-[state=active]:hover:bg-blue-600",children:[(0,a.jsx)(h.A,{className:"w-4 h-4"}),(0,a.jsx)("span",{className:"hidden sm:inline",children:"文件传输"}),(0,a.jsx)("span",{className:"sm:hidden",children:"文件"})]}),(0,a.jsxs)(x,{value:"text",className:"flex items-center justify-center space-x-2 px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 hover:bg-slate-50 data-[state=active]:bg-emerald-500 data-[state=active]:text-white data-[state=active]:shadow-md data-[state=active]:hover:bg-emerald-600",children:[(0,a.jsx)(g.A,{className:"w-4 h-4"}),(0,a.jsx)("span",{className:"hidden sm:inline",children:"文本传输"}),(0,a.jsx)("span",{className:"sm:hidden",children:"文本"})]}),(0,a.jsxs)(x,{value:"desktop",className:"flex items-center justify-center space-x-2 px-4 py-3 text-sm font-medium rounded-lg transition-all duration-200 hover:bg-slate-50 data-[state=active]:bg-purple-500 data-[state=active]:text-white data-[state=active]:shadow-md data-[state=active]:hover:bg-purple-600",children:[(0,a.jsx)(p.A,{className:"w-4 h-4"}),(0,a.jsx)("span",{className:"hidden sm:inline",children:"共享桌面"}),(0,a.jsx)("span",{className:"sm:hidden",children:"桌面"})]})]})}),(0,a.jsxs)("div",{children:[(0,a.jsx)(u,{value:"webrtc",className:"mt-0 animate-fade-in-up",children:(0,a.jsx)(q,{})}),(0,a.jsx)(u,{value:"text",className:"mt-0 animate-fade-in-up",children:(0,a.jsx)(F,{onSendText:async e=>{try{let s=await fetch("/api/create-text-room",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({text:e})}),t=await s.json();if(!s.ok)throw Error(t.error||"创建文本房间失败");return t.code}catch(e){throw console.error("创建文本房间失败:",e),e}},onReceiveText:async e=>{try{let s=await fetch("/api/get-text-content?code=".concat(e)),t=await s.json();if(!s.ok)throw Error(t.error||"获取文本内容失败");return t.text}catch(e){throw console.error("获取文本内容失败:",e),e}}})}),(0,a.jsx)(u,{value:"desktop",className:"mt-0 animate-fade-in-up",children:(0,a.jsx)(W,{})})]})]})})]})})}let V=function(){return(0,a.jsx)(l.Suspense,{fallback:(0,a.jsx)("div",{className:"min-h-screen flex items-center justify-center",children:"加载中..."}),children:(0,a.jsx)(G,{})})}}},e=>{e.O(0,[423,441,964,358],()=>e(e.s=409)),_N_E=e.O()}]);
\ No newline at end of file
diff --git a/internal/web/frontend/index.html b/internal/web/frontend/index.html
index 625f24c..2d051d3 100644
--- a/internal/web/frontend/index.html
+++ b/internal/web/frontend/index.html
@@ -1 +1 @@
-File Transfer - 文件传输
加载中...
\ No newline at end of file
+File Transfer - 文件传输
加载中...
\ No newline at end of file
diff --git a/internal/web/frontend/index.txt b/internal/web/frontend/index.txt
index 3f75408..9f8725d 100644
--- a/internal/web/frontend/index.txt
+++ b/internal/web/frontend/index.txt
@@ -2,7 +2,7 @@
2:I[6801,["177","static/chunks/app/layout-d0f2a5cbcfca20f5.js"],"ToastProvider"]
3:I[7555,[],""]
4:I[1295,[],""]
-5:I[1287,["423","static/chunks/423-3db9dd818e8fd852.js","974","static/chunks/app/page-6f704b6eb3095813.js"],"default"]
+5:I[7443,["423","static/chunks/423-3db9dd818e8fd852.js","974","static/chunks/app/page-c710cf440dafbd05.js"],"default"]
6:I[9665,[],"OutletBoundary"]
8:I[4911,[],"AsyncMetadataOutlet"]
a:I[9665,[],"ViewportBoundary"]
@@ -12,7 +12,7 @@ f:I[8393,[],""]
:HL["/_next/static/media/569ce4b8f30dc480-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
:HL["/_next/static/media/93f479601ee12b01-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
:HL["/_next/static/css/f6f47fde0030ec04.css","style"]
-0:{"P":null,"b":"8bbWAyBmnmNj0Jk5ryXl4","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/f6f47fde0030ec04.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L5",null,{}],null,["$","$L6",null,{"children":["$L7",["$","$L8",null,{"promise":"$@9"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$La",null,{"children":"$Lb"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$Lc",null,{"children":["$","div",null,{"hidden":true,"children":["$","$d",null,{"fallback":null,"children":"$Le"}]}]}]]}],false]],"m":"$undefined","G":["$f",[]],"s":false,"S":true}
+0:{"P":null,"b":"8y8TXPJV_4IIplGqW3zUm","p":"","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/f6f47fde0030ec04.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"zh-CN","children":["$","body",null,{"className":"__variable_5cfdac __variable_9a8899 antialiased","children":["$","$L2",null,{"children":["$","$L3",null,{"parallelRouterKey":"children","error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L4",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]],"forbidden":"$undefined","unauthorized":"$undefined"}]}]}]}]]}],{"children":["__PAGE__",["$","$1","c",{"children":[["$","$L5",null,{}],null,["$","$L6",null,{"children":["$L7",["$","$L8",null,{"promise":"$@9"}]]}]]}],{},null,false]},null,false],["$","$1","h",{"children":[null,[["$","$La",null,{"children":"$Lb"}],["$","meta",null,{"name":"next-size-adjust","content":""}]],["$","$Lc",null,{"children":["$","div",null,{"hidden":true,"children":["$","$d",null,{"fallback":null,"children":"$Le"}]}]}]]}],false]],"m":"$undefined","G":["$f",[]],"s":false,"S":true}
b:[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
7:null
10:I[8175,[],"IconMark"]