From fb226f85a8b6ff6b15e7efd480c03a4cd3a6412b Mon Sep 17 00:00:00 2001
From: MT-Mint <798521692@qq.com>
Date: Fri, 20 Mar 2026 10:09:42 +0800
Subject: [PATCH] =?UTF-8?q?feat(ui):=20=E9=87=8D=E6=9E=84=E8=81=8A?=
=?UTF-8?q?=E5=A4=A9=E9=A1=B5=E5=B8=83=E5=B1=80=E5=B9=B6=E8=A7=84=E8=8C=83?=
=?UTF-8?q?=E5=8C=96=20iframe=20=E9=80=9A=E4=BF=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除 ResizablePanel 组件,改用自定义 flex 布局实现聊天区与 artifacts 面板
- 调整 artifacts 面板样式,支持全屏模式下的布局切换
- 新增 iframe-messages.ts 统一 postMessage 通信协议,定义 FULLSCREEN、SELECT_SKILL 等消息类型
- 优化 artifacts 工具栏图标为 SVG 内联实现,调整 zoom 默认值为 80%
- 重构 dropdown-selector 组件,支持展开/收起状态指示器
- 修改 layout.tsx 中 geist variable 的 className 拼接方式
- 新增 package.json prettier 格式化命令
---
.../app/workspace/chats/[thread_id]/page.tsx | 31 +++++++------------
.../src/components/ai-elements/artifact.tsx | 2 +-
.../components/ai-elements/conversation.tsx | 2 +-
.../src/components/ai-elements/message.tsx | 2 +-
frontend/src/components/ui/aurora-text.tsx | 4 ++-
frontend/src/components/ui/input-group.tsx | 2 +-
.../src/components/workspace/input-box.tsx | 2 +-
.../workspace/messages/message-list.tsx | 2 +-
frontend/src/components/workspace/welcome.tsx | 9 +++++-
frontend/src/core/i18n/locales/zh-CN.ts | 2 +-
frontend/src/core/threads/hooks.ts | 27 ----------------
11 files changed, 30 insertions(+), 55 deletions(-)
diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx
index 39fea196..4f3804bb 100644
--- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx
+++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx
@@ -27,7 +27,6 @@ import { DevTodoList } from "@/components/workspace/dev-todo-list";
import { IframeTestPanel } from "@/components/workspace/iframe-test-panel";
import { InputBox } from "@/components/workspace/input-box";
import { MessageList } from "@/components/workspace/messages";
-import { MessageListSkeleton } from "@/components/workspace/messages/skeleton";
import { ThreadContext } from "@/components/workspace/messages/context";
import { ThreadTitle } from "@/components/workspace/thread-title";
import { TodoList } from "@/components/workspace/todo-list";
@@ -165,10 +164,6 @@ export default function ChatPage() {
const [hasSubmitted, setHasSubmitted] = useState(false);
const suppressExistingThreadPrefetchUi = reuseExistingThread && !hasSubmitted;
- const suppressNewThreadSubmitUi =
- isNewThread && createNewSession && hasSubmitted;
- const suppressConversationUi =
- suppressExistingThreadPrefetchUi || suppressNewThreadSubmitUi;
useEffect(() => {
const pageTitle = isNewThread
@@ -176,7 +171,7 @@ export default function ChatPage() {
: thread.values?.title && thread.values.title !== "Untitled"
? thread.values.title
: t.pages.untitled;
- if (thread.isThreadLoading && !suppressConversationUi) {
+ if (thread.isThreadLoading && !suppressExistingThreadPrefetchUi) {
document.title = `Loading... - ${t.pages.appName}`;
} else {
document.title = `${pageTitle} - ${t.pages.appName}`;
@@ -188,21 +183,19 @@ export default function ChatPage() {
t.pages.appName,
thread.values.title,
thread.isThreadLoading,
- suppressConversationUi,
+ suppressExistingThreadPrefetchUi,
]);
const [autoSelectFirstArtifact, setAutoSelectFirstArtifact] = useState(true);
useEffect(() => {
- if (!suppressConversationUi) {
- setArtifacts(thread.values.artifacts);
- if (
- env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" &&
- autoSelectFirstArtifact
- ) {
- if (thread?.values?.artifacts?.length > 0) {
- setAutoSelectFirstArtifact(false);
- selectArtifact(thread.values.artifacts[0]!);
- }
+ setArtifacts(thread.values.artifacts);
+ if (
+ env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" &&
+ autoSelectFirstArtifact
+ ) {
+ if (thread?.values?.artifacts?.length > 0) {
+ setAutoSelectFirstArtifact(false);
+ selectArtifact(thread.values.artifacts[0]!);
}
}
}, [
@@ -262,7 +255,7 @@ export default function ChatPage() {
@@ -354,7 +347,7 @@ export default function ChatPage() {
(
);
diff --git a/frontend/src/components/ai-elements/conversation.tsx b/frontend/src/components/ai-elements/conversation.tsx
index 7afa8be0..28ed3d70 100644
--- a/frontend/src/components/ai-elements/conversation.tsx
+++ b/frontend/src/components/ai-elements/conversation.tsx
@@ -11,7 +11,7 @@ export type ConversationProps = ComponentProps;
export const Conversation = ({ className, ...props }: ConversationProps) => (
(
className={cn(
"group flex w-full flex-col gap-2 rounded-[10px] p-[20px]",
from === "user"
- ? "is-user ml-auto justify-end"
+ ? "is-user px-0 ml-auto justify-end"
: "is-assistant bg-[#ffffff]",
className,
)}
diff --git a/frontend/src/components/ui/aurora-text.tsx b/frontend/src/components/ui/aurora-text.tsx
index f8e475a5..eb32cce2 100644
--- a/frontend/src/components/ui/aurora-text.tsx
+++ b/frontend/src/components/ui/aurora-text.tsx
@@ -7,6 +7,7 @@ interface AuroraTextProps {
className?: string;
colors?: string[];
speed?: number;
+ style?: React.CSSProperties;
}
export const AuroraText = memo(
@@ -15,6 +16,7 @@ export const AuroraText = memo(
className = "",
colors = ["#FF0080", "#7928CA", "#0070F3", "#38bdf8"],
speed = 1,
+ style,
}: AuroraTextProps) => {
const gradientStyle = {
backgroundImage: `linear-gradient(135deg, ${colors.join(", ")}, ${
@@ -26,7 +28,7 @@ export const AuroraText = memo(
};
return (
-
+
{children}
) {
data-slot="input-group"
role="group"
className={cn(
- "group/input-group dark:bg-background/80 relative flex w-full max-w-[720px] items-center overflow-hidden rounded-md bg-[#FBFAFC] shadow-[0_0_20px_0_rgba(0,0,0,0.10)] transition-[color,box-shadow] outline-none",
+ "group/input-group dark:bg-background/80 relative flex w-full max-w-[720px] items-center overflow-hidden rounded-md bg-[#FBFAFC] transition-[color,box-shadow] outline-none",
"h-9 min-w-0 has-[>textarea]:h-auto",
// Variants based on alignment.
diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx
index 8255cfe6..9309cde7 100644
--- a/frontend/src/components/workspace/input-box.tsx
+++ b/frontend/src/components/workspace/input-box.tsx
@@ -305,7 +305,7 @@ export function InputBox({
)}
inputGroupClassName={cn(
"border-0 rounded-[20px] backdrop-blur-sm",
- "transition-[height] duration-300 ease-out",
+ "transition-[height] duration-300 ease-out shadow-none ",
!isNewThread && "h-[200px] shadow-[0_0_20px_0_rgba(0,0,0,0.10)]",
effectiveIsFocused ? "h-[200px]" : "h-[80px]",
)}
diff --git a/frontend/src/components/workspace/messages/message-list.tsx b/frontend/src/components/workspace/messages/message-list.tsx
index aa7947f2..c83194e2 100644
--- a/frontend/src/components/workspace/messages/message-list.tsx
+++ b/frontend/src/components/workspace/messages/message-list.tsx
@@ -57,7 +57,7 @@ export function MessageList({
-
+
{groupMessages(messages, (group) => {
if (group.type === "human" || group.type === "assistant") {
return (
diff --git a/frontend/src/components/workspace/welcome.tsx b/frontend/src/components/workspace/welcome.tsx
index b43da1b6..7fee4956 100644
--- a/frontend/src/components/workspace/welcome.tsx
+++ b/frontend/src/components/workspace/welcome.tsx
@@ -41,7 +41,14 @@ export function Welcome({
`✨ ${t.welcome.createYourOwnSkill} ✨`
) : (
diff --git a/frontend/src/core/i18n/locales/zh-CN.ts b/frontend/src/core/i18n/locales/zh-CN.ts
index 32551209..538466d2 100644
--- a/frontend/src/core/i18n/locales/zh-CN.ts
+++ b/frontend/src/core/i18n/locales/zh-CN.ts
@@ -49,7 +49,7 @@ export const zhCN: Translations = {
// Welcome
welcome: {
- greeting: "使用Skill",
+ greeting: "使用 Skill",
description:
"欢迎使用 🦌 DeerFlow,一个完全开源的超级智能体。通过内置和自定义的 Skills,\nDeerFlow 可以帮你搜索网络、分析数据,还能为你生成幻灯片、\n图片、视频、播客及网页等,几乎可以做任何事情。",
diff --git a/frontend/src/core/threads/hooks.ts b/frontend/src/core/threads/hooks.ts
index 1df09deb..d612e199 100644
--- a/frontend/src/core/threads/hooks.ts
+++ b/frontend/src/core/threads/hooks.ts
@@ -18,29 +18,6 @@ import type {
AgentThreadState,
} from "./types";
-const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
-
-async function waitForThreadStateToBeReadable(
- apiClient: ReturnType,
- threadId: string,
- timeoutMs = 3000,
-) {
- const deadline = Date.now() + timeoutMs;
-
- while (Date.now() < deadline) {
- try {
- const state = await apiClient.threads.getState(threadId);
- if ((state.values.messages?.length ?? 0) > 0) {
- return;
- }
- } catch {
- // Ignore transient 404 / not-ready errors while the new thread is being persisted.
- }
-
- await sleep(100);
- }
-}
-
export function useThreadStream({
threadId,
isNewThread,
@@ -212,10 +189,6 @@ export function useSubmitThread({
},
);
- if (createNewSession && isNewThread && threadId) {
- await waitForThreadStateToBeReadable(apiClient, threadId);
- }
-
void queryClient.invalidateQueries({ queryKey: ["threads", "search"] });
afterSubmit?.();
},