From 32f581cf50f354c0a303b5d755a97a79930b1754 Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Tue, 17 Mar 2026 16:40:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=85=A8=E5=B1=8F=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/workspace/chats/[thread_id]/page.tsx | 19 +- .../src/components/ai-elements/artifact.tsx | 2 +- .../artifacts/artifact-file-detail.tsx | 168 ++++++++++++++++-- .../workspace/artifacts/context.tsx | 7 + .../components/workspace/chats/chat-box.tsx | 16 +- frontend/src/core/i18n/locales/types.ts | 2 + frontend/src/core/i18n/locales/zh-CN.ts | 2 + 7 files changed, 194 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 4b44613f..3e24b019 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -24,6 +24,7 @@ import { MessageList } from "@/components/workspace/messages"; import { ThreadContext } from "@/components/workspace/messages/context"; import { ThreadTitle } from "@/components/workspace/thread-title"; import { Welcome } from "@/components/workspace/welcome"; +import { useArtifacts } from "@/components/workspace/artifacts"; import { useI18n } from "@/core/i18n/hooks"; import { useNotification } from "@/core/notification/hooks"; import { useLocalSettings } from "@/core/settings"; @@ -36,6 +37,7 @@ export default function ChatPage() { const { t } = useI18n(); const [settings, setSettings] = useLocalSettings(); const [showExitDialog, setShowExitDialog] = useState(false); + const { fullscreen } = useArtifacts(); const { threadId, isNewThread, setIsNewThread, isMock } = useThreadChat(); useSpecificChatMode(); @@ -85,7 +87,7 @@ export default function ChatPage() {
- Todo + To-dos } /> @@ -147,14 +149,19 @@ export default function ChatPage() {
-
+
; export const Artifact = ({ className, ...props }: ArtifactProps) => (
{ return filepathFromProps.startsWith("write-file:"); }, [filepathFromProps]); @@ -105,6 +107,33 @@ export function ArtifactFileDetail({ const [isInstalling, setIsInstalling] = useState(false); const [zoom, setZoom] = useState(100); const { isMock } = useThread(); + + // 全屏切换处理 + const handleFullscreenToggle = useCallback(() => { + if (!document.fullscreenElement) { + document.documentElement.requestFullscreen().catch((err) => { + console.error("无法进入全屏模式:", err); + }); + setFullscreen(true); + } else { + document.exitFullscreen().catch((err) => { + console.error("无法退出全屏模式:", err); + }); + setFullscreen(false); + } + }, [setFullscreen]); + + // 监听全屏变化 + useEffect(() => { + const handleFullscreenChange = () => { + setFullscreen(!!document.fullscreenElement); + }; + document.addEventListener("fullscreenchange", handleFullscreenChange); + return () => { + document.removeEventListener("fullscreenchange", handleFullscreenChange); + }; + }, [setFullscreen]); + useEffect(() => { if (isSupportPreview) { setViewMode("preview"); @@ -172,11 +201,10 @@ export function ArtifactFileDetail({ )}
-
+
{/* 放大缩小选择器 */} - {/* 新界面打开的按钮 */} {/* {!isWriteFile && filepath.endsWith(".skill") && ( )} */} - {!isWriteFile && ( + {/* 新界面打开的按钮 */} + {/* {!isWriteFile && ( - )} + )} */} + {/* 复制按钮 */} {isCodeFile && ( { @@ -215,26 +244,143 @@ export function ArtifactFileDetail({ } }} tooltip={t.clipboard.copyToClipboard} - /> + > + + + + + )} + {/* 下载按钮 */} {!isWriteFile && ( + > + + + + + )} + {/* 全屏按钮 */} + + {fullscreen ? ( + + + + + + + ) : ( + + + + )} + + {/* 关闭按钮 */} setOpen(false)} tooltip={t.common.close} - /> + > + + + +
diff --git a/frontend/src/components/workspace/artifacts/context.tsx b/frontend/src/components/workspace/artifacts/context.tsx index af9b19ab..b2e67a76 100644 --- a/frontend/src/components/workspace/artifacts/context.tsx +++ b/frontend/src/components/workspace/artifacts/context.tsx @@ -21,6 +21,9 @@ export interface ArtifactsContextType { open: boolean; autoOpen: boolean; setOpen: (open: boolean) => void; + + fullscreen: boolean; + setFullscreen: (fullscreen: boolean) => void; } const ArtifactsContext = createContext( @@ -39,6 +42,7 @@ export function ArtifactsProvider({ children }: ArtifactsProviderProps) { env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true", ); const [autoOpen, setAutoOpen] = useState(true); + const [fullscreen, setFullscreen] = useState(false); const { setOpen: setSidebarOpen } = useSidebar(); const select = useCallback( @@ -78,6 +82,9 @@ export function ArtifactsProvider({ children }: ArtifactsProviderProps) { selectedArtifact, select, deselect, + + fullscreen, + setFullscreen, }; return ( diff --git a/frontend/src/components/workspace/chats/chat-box.tsx b/frontend/src/components/workspace/chats/chat-box.tsx index 59b12a35..f0d5c070 100644 --- a/frontend/src/components/workspace/chats/chat-box.tsx +++ b/frontend/src/components/workspace/chats/chat-box.tsx @@ -21,6 +21,7 @@ import { } from "../artifacts"; import { useThread } from "../messages/context"; +const FULLSCREEN_MODE = { chat: 0, artifacts: 100 }; const CLOSE_MODE = { chat: 100, artifacts: 0 }; const OPEN_MODE = { chat: 50, artifacts: 50 }; @@ -40,6 +41,7 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({ select: selectArtifact, deselect, selectedArtifact, + fullscreen, } = useArtifacts(); const [autoSelectFirstArtifact, setAutoSelectFirstArtifact] = useState(true); @@ -88,13 +90,15 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({ useEffect(() => { if (layoutRef.current) { - if (artifactPanelOpen) { + if (fullscreen) { + layoutRef.current.setLayout(FULLSCREEN_MODE); + } else if (artifactPanelOpen) { layoutRef.current.setLayout(OPEN_MODE); } else { layoutRef.current.setLayout(CLOSE_MODE); } } - }, [artifactPanelOpen]); + }, [artifactPanelOpen, fullscreen]); return ( = ({ groupRef={layoutRef} > @@ -111,8 +118,9 @@ const ChatBox: React.FC<{ children: React.ReactNode; threadId: string }> = ({