"use client"; import { useSearchParams, useRouter } from "next/navigation"; import { useEffect, useRef, useState, type PointerEvent as ReactPointerEvent, } from "react"; import { Button } from "@/components/ui/button"; import { POST_MESSAGE_TYPES, sendToParent } from "@/core/iframe-messages"; import { useIframeSkill } from "@/hooks/use-iframe-skill"; import { copyToClipboard } from "@/lib/utils"; import { cn } from "@/lib/utils"; /** * IframeTestPanel —— 仅用于开发阶段测试 iframe 通信功能 * * 测试场景: * 1. mode=skill 侧边栏隐藏 * 2. useSpecificChatMode 注入提示词 * 3. sendSelectSkill / openSkillDialog / clearSkill */ export function IframeTestPanel() { const router = useRouter(); const searchParams = useSearchParams(); const iframeSkill = useIframeSkill(); const [log, setLog] = useState([]); const [open, setOpen] = useState(true); const [collapsed, setCollapsed] = useState(false); const [position, setPosition] = useState<{ x: number; y: number } | null>( null, ); const [dragging, setDragging] = useState(false); const panelRef = useRef(null); const dragOffsetRef = useRef({ x: 0, y: 0 }); const panelSizeRef = useRef({ width: 0, height: 0 }); const isSkillMode = searchParams.get("mode") === "skill"; function addLog(msg: string) { setLog((prev) => [ `[${new Date().toLocaleTimeString()}] ${msg}`, ...prev.slice(0, 9), ]); } function handleEnterSkillMode() { router.push(`?mode=skill`); addLog("进入 mode=skill,URL 已更新"); } function handleExitSkillMode() { router.push(`?`); addLog("退出 skill 模式"); } function handleSendSelectSkill() { iframeSkill.sendSelectSkill([{ id: "skill_001", name: "测试技能1" }]); addLog( "postMessage → selectedSkills ([{id:'skill_001',name:'测试技能1'}])", ); } function handleSendSelectSkillArray() { iframeSkill.sendSelectSkill([ { id: "1246", name: "技能A" }, { id: "1247", name: "技能B" }, { id: "1248", name: "技能C" }, ]); addLog("postMessage → selectedSkills (3 skills)"); } function handleOpenSkillDialog() { iframeSkill.openSkillDialog(); addLog("postMessage → openSkillDialog"); } function handleClearSkill() { iframeSkill.clearSkill(); addLog("clearSkill 已调用,postMessage → selectedSkills=[]"); } function handleTestClipboardCopy() { const testText = "测试复制内容 - " + new Date().toISOString(); void copyToClipboard(testText); addLog(`copyToClipboard → "${testText.slice(0, 30)}..."`); } function handleSendIsChatting(isChatting: boolean) { sendToParent({ type: POST_MESSAGE_TYPES.IS_CHATTING, isChatting: isChatting, }); addLog(`postMessage → is_chatting (${isChatting})`); } function handlePointerDown(event: ReactPointerEvent) { if (!panelRef.current) return; const rect = panelRef.current.getBoundingClientRect(); panelSizeRef.current = { width: rect.width, height: rect.height }; dragOffsetRef.current = { x: event.clientX - rect.left, y: event.clientY - rect.top, }; setPosition({ x: rect.left, y: rect.top }); setDragging(true); event.currentTarget.setPointerCapture(event.pointerId); } useEffect(() => { if (!dragging) return; const handleMove = (event: PointerEvent) => { const { width, height } = panelSizeRef.current; const nextX = event.clientX - dragOffsetRef.current.x; const nextY = event.clientY - dragOffsetRef.current.y; const clampedX = Math.min( Math.max(8, nextX), Math.max(8, window.innerWidth - width - 8), ); const clampedY = Math.min( Math.max(8, nextY), Math.max(8, window.innerHeight - height - 8), ); setPosition({ x: clampedX, y: clampedY }); }; const handleUp = () => { setDragging(false); }; window.addEventListener("pointermove", handleMove); window.addEventListener("pointerup", handleUp); return () => { window.removeEventListener("pointermove", handleMove); window.removeEventListener("pointerup", handleUp); }; }, [dragging]); // 检测是否在 iframe 中 const isInIframe = typeof window !== "undefined" && window.self !== window.top; if (!open) { return ( ); } return (
{/* 标题栏 */}
🧪 iframe 通信测试
{!collapsed && (
{/* 当前状态 */}
当前状态
mode: {isSkillMode ? "skill ✅" : "普通"} selectedSkill: {iframeSkill.selectedSkill ? `${iframeSkill.selectedSkill.skill_id} / ${iframeSkill.selectedSkill.title}` : "无"}
{/* 场景 1:侧边栏隐藏 */}
① 侧边栏隐藏(layout)
{/* 场景 2:skill 选择通信 */}
② postMessage 通信(发送到宿主)
{/* 场景 3:接收宿主页 selectedSkill */}
③ 接收宿主页 selectedSkill
{/* 场景 4:剪贴板复制(iframe 通信) */}
④ 剪贴板复制(iframe 通信) {isInIframe ? "iframe 模式" : "独立页面"}
{isInIframe ? "将通过 postMessage 请求父页面复制" : "将直接调用 navigator.clipboard"}
{/* 场景 5:is_chatting */}
⑤ is_chatting
{/* 日志 */} {log.length > 0 && (
操作日志
{log.map((l, i) => (
{l}
))}
)}
)}
); }