From 1fd5405f42694b7d5f0481d31b7c1c9f80b15206 Mon Sep 17 00:00:00 2001 From: SuperManTouX <93423476+SuperManTouX@users.noreply.github.com> Date: Fri, 13 Mar 2026 20:13:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=88=9D=E6=AD=A5=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/.vscode/settings.json | 3 - .../user-data/outputs/style.css | 2 +- .../app/workspace/chats/[thread_id]/page.tsx | 105 +-- frontend/src/components/ui/sidebar.tsx | 5 +- .../components/workspace/chats/chat-box.tsx | 6 +- .../components/workspace/dev-todo-list.tsx | 61 ++ .../src/components/workspace/flip-display.tsx | 2 +- .../src/components/workspace/input-box.tsx | 619 +++++++++--------- .../workspace/messages/message-list.tsx | 1 - frontend/src/styles/globals.css | 4 +- scripts/cleanup-containers.sh | 0 scripts/docker.sh | 0 .../find-skills/scripts/install-skill.sh | 0 .../skill-creator/scripts/init_skill.py | 0 .../skill-creator/scripts/package_skill.py | 0 .../skill-creator/scripts/quick_validate.py | 0 .../vercel-deploy-claimable/scripts/deploy.sh | 0 17 files changed, 447 insertions(+), 361 deletions(-) delete mode 100644 frontend/.vscode/settings.json create mode 100644 frontend/src/components/workspace/dev-todo-list.tsx mode change 100755 => 100644 scripts/cleanup-containers.sh mode change 100755 => 100644 scripts/docker.sh mode change 100755 => 100644 skills/public/find-skills/scripts/install-skill.sh mode change 100755 => 100644 skills/public/skill-creator/scripts/init_skill.py mode change 100755 => 100644 skills/public/skill-creator/scripts/package_skill.py mode change 100755 => 100644 skills/public/skill-creator/scripts/quick_validate.py mode change 100755 => 100644 skills/public/vercel-deploy-claimable/scripts/deploy.sh diff --git a/frontend/.vscode/settings.json b/frontend/.vscode/settings.json deleted file mode 100644 index 19bd6ebc..00000000 --- a/frontend/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "window.title": "${activeEditorShort}${separator}${separator}deer-flow/frontend" -} diff --git a/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/style.css b/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/style.css index 0673ed21..93843998 100644 --- a/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/style.css +++ b/frontend/public/demo/threads/7cfa5f8f-a2f8-47ad-acbd-da7137baf990/user-data/outputs/style.css @@ -87,7 +87,7 @@ body { font-size: 16px; line-height: 1.6; color: var(--text-primary); - background-color: var(--bg-primary); + /* background-color: var(--bg-primary); */ transition: background-color var(--transition-normal), color var(--transition-normal); diff --git a/frontend/src/app/workspace/chats/[thread_id]/page.tsx b/frontend/src/app/workspace/chats/[thread_id]/page.tsx index 3147358a..4ce7c0b0 100644 --- a/frontend/src/app/workspace/chats/[thread_id]/page.tsx +++ b/frontend/src/app/workspace/chats/[thread_id]/page.tsx @@ -3,17 +3,18 @@ import { useCallback } from "react"; import { type PromptInputMessage } from "@/components/ai-elements/prompt-input"; +import { Button } from "@/components/ui/button"; import { ArtifactTrigger } from "@/components/workspace/artifacts"; import { ChatBox, useSpecificChatMode, useThreadChat, } from "@/components/workspace/chats"; +import { DevTodoList } from "@/components/workspace/dev-todo-list"; import { InputBox } from "@/components/workspace/input-box"; import { MessageList } from "@/components/workspace/messages"; import { ThreadContext } from "@/components/workspace/messages/context"; import { ThreadTitle } from "@/components/workspace/thread-title"; -import { TodoList } from "@/components/workspace/todo-list"; import { Welcome } from "@/components/workspace/welcome"; import { useI18n } from "@/core/i18n/hooks"; import { useNotification } from "@/core/notification/hooks"; @@ -72,19 +73,38 @@ export default function ChatPage() { return ( -
+
-
+ {/* 返回查看结果左箭头 */} +
+ + + + +
+
-
+
+
@@ -96,51 +116,40 @@ export default function ChatPage() { thread={thread} />
-
-
-
-
-
-
- - } - disabled={env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true"} - onContextChange={(context) => setSettings("context", context)} - onSubmit={handleSubmit} - onStop={handleStop} - /> - {env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && ( -
- {t.common.notAvailableInDemoMode} -
- )} -
-
+
+
+ + } + disabled={env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true"} + onContextChange={(context) => setSettings("context", context)} + onSubmit={handleSubmit} + onStop={handleStop} + /> + {env.NEXT_PUBLIC_STATIC_WEBSITE_ONLY === "true" && ( +
+ {t.common.notAvailableInDemoMode} +
+ )} +
+
); diff --git a/frontend/src/components/ui/sidebar.tsx b/frontend/src/components/ui/sidebar.tsx index c232e36c..bff1bfcd 100644 --- a/frontend/src/components/ui/sidebar.tsx +++ b/frontend/src/components/ui/sidebar.tsx @@ -139,7 +139,7 @@ function SidebarProvider({ } as React.CSSProperties } className={cn( - "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full", + "group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full m-auto rounded-t-[20px] overflow-hidden", className, )} {...props} @@ -207,6 +207,7 @@ function Sidebar({ return (
) {
= ({ defaultLayout={{ chat: 100, artifacts: 0 }} groupRef={layoutRef} > - + {children} = ({ />
diff --git a/frontend/src/components/workspace/dev-todo-list.tsx b/frontend/src/components/workspace/dev-todo-list.tsx new file mode 100644 index 00000000..e05a4553 --- /dev/null +++ b/frontend/src/components/workspace/dev-todo-list.tsx @@ -0,0 +1,61 @@ +"use client"; + +import { ListTodoIcon } from "lucide-react"; + +import type { Todo } from "@/core/todos"; + +import { + QueueItem, + QueueItemContent, + QueueItemIndicator, + QueueList, +} from "../ai-elements/queue"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; + +export function DevTodoList({ + className, + todos, + trigger, + hidden, +}: { + className?: string; + todos: Todo[]; + trigger: React.ReactNode; + hidden: boolean; +}) { + return ( + + {trigger} + + + {todos.map((todo, i) => ( + +
+ + + {todo.content} + +
+
+ ))} +
+
+
+ ); +} + +export { ListTodoIcon }; diff --git a/frontend/src/components/workspace/flip-display.tsx b/frontend/src/components/workspace/flip-display.tsx index 19a42295..1b680534 100644 --- a/frontend/src/components/workspace/flip-display.tsx +++ b/frontend/src/components/workspace/flip-display.tsx @@ -19,7 +19,7 @@ export function FlipDisplay({ initial={{ y: 8, opacity: 0 }} animate={{ y: 2, opacity: 1 }} exit={{ y: -8, opacity: 0 }} - transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }} + // transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }} > {children} diff --git a/frontend/src/components/workspace/input-box.tsx b/frontend/src/components/workspace/input-box.tsx index b6e6a688..79ef6b09 100644 --- a/frontend/src/components/workspace/input-box.tsx +++ b/frontend/src/components/workspace/input-box.tsx @@ -146,6 +146,7 @@ export function InputBox({ const { thread, isMock } = useThread(); const { textInput } = usePromptInputController(); const promptRootRef = useRef(null); + const textareaRef = useRef(null); const [followups, setFollowups] = useState([]); const [followupsHidden, setFollowupsHidden] = useState(false); @@ -158,6 +159,22 @@ export function InputBox({ null, ); + const [isFocused, setIsFocused] = useState(false); + const containerRef = useRef(null); + + useEffect(() => { + if (!isFocused) return; + + const handleClickOutside = (event: MouseEvent) => { + if (containerRef.current && !containerRef.current.contains(event.target as Node)) { + setIsFocused(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, [isFocused]); + useEffect(() => { if (models.length === 0) { return; @@ -372,10 +389,11 @@ export function InputBox({ }, [context.model_name, disabled, isMock, status, thread.messages, threadId]); return ( -
+
{ promptRootRef.current = el; containerRef.current = el; }} className="relative"> {(attachment) => } - + - + {!isFocused && ( +
{ + setIsFocused(true); + textareaRef.current?.focus(); + }} + /> + )} + - {/* TODO: Add more connectors here - - - - - - */} - - - - -
- {context.mode === "flash" && } - {context.mode === "thinking" && ( - - )} - {context.mode === "pro" && ( - - )} - {context.mode === "ultra" && ( - - )} -
-
- {(context.mode === "flash" && t.inputBox.flashMode) || - (context.mode === "thinking" && t.inputBox.reasoningMode) || - (context.mode === "pro" && t.inputBox.proMode) || - (context.mode === "ultra" && t.inputBox.ultraMode)} -
-
-
- - - - {t.inputBox.mode} - - - handleModeSelect("flash")} - > -
-
- - {t.inputBox.flashMode} -
-
- {t.inputBox.flashModeDescription} -
-
- {context.mode === "flash" ? ( - - ) : ( -
- )} - - {supportThinking && ( - handleModeSelect("thinking")} - > -
-
- - {t.inputBox.reasoningMode} -
-
- {t.inputBox.reasoningModeDescription} -
-
- {context.mode === "thinking" ? ( - - ) : ( -
- )} - - )} - handleModeSelect("pro")} - > -
-
- - {t.inputBox.proMode} -
-
- {t.inputBox.proModeDescription} -
-
- {context.mode === "pro" ? ( - - ) : ( -
- )} - - handleModeSelect("ultra")} - > -
-
- -
- {t.inputBox.ultraMode} -
-
-
- {t.inputBox.ultraModeDescription} -
-
- {context.mode === "ultra" ? ( - - ) : ( -
- )} - - - - - - {supportReasoningEffort && context.mode !== "flash" && ( + - -
- {t.inputBox.reasoningEffort}: - {context.reasoning_effort === "minimal" && " " + t.inputBox.reasoningEffortMinimal} - {context.reasoning_effort === "low" && " " + t.inputBox.reasoningEffortLow} - {context.reasoning_effort === "medium" && " " + t.inputBox.reasoningEffortMedium} - {context.reasoning_effort === "high" && " " + t.inputBox.reasoningEffortHigh} -
-
- + + +
+ {context.mode === "flash" && } + {context.mode === "thinking" && ( + + )} + {context.mode === "pro" && ( + + )} + {context.mode === "ultra" && ( + + )} +
+
+ {(context.mode === "flash" && t.inputBox.flashMode) || + (context.mode === "thinking" && t.inputBox.reasoningMode) || + (context.mode === "pro" && t.inputBox.proMode) || + (context.mode === "ultra" && t.inputBox.ultraMode)} +
+
+
+ - {t.inputBox.reasoningEffort} + {t.inputBox.mode} handleReasoningEffortSelect("minimal")} + onSelect={() => handleModeSelect("flash")} >
- {t.inputBox.reasoningEffortMinimal} + + {t.inputBox.flashMode}
-
- {t.inputBox.reasoningEffortMinimalDescription} +
+ {t.inputBox.flashModeDescription}
- {context.reasoning_effort === "minimal" ? ( + {context.mode === "flash" ? ( + + ) : ( +
+ )} + + {supportThinking && ( + handleModeSelect("thinking")} + > +
+
+ + {t.inputBox.reasoningMode} +
+
+ {t.inputBox.reasoningModeDescription} +
+
+ {context.mode === "thinking" ? ( + + ) : ( +
+ )} + + )} + handleModeSelect("pro")} + > +
+
+ + {t.inputBox.proMode} +
+
+ {t.inputBox.proModeDescription} +
+
+ {context.mode === "pro" ? ( ) : (
@@ -625,65 +567,33 @@ export function InputBox({ handleReasoningEffortSelect("low")} + onSelect={() => handleModeSelect("ultra")} >
- {t.inputBox.reasoningEffortLow} + +
+ {t.inputBox.ultraMode} +
-
- {t.inputBox.reasoningEffortLowDescription} +
+ {t.inputBox.ultraModeDescription}
- {context.reasoning_effort === "low" ? ( - - ) : ( -
- )} - - handleReasoningEffortSelect("medium")} - > -
-
- {t.inputBox.reasoningEffortMedium} -
-
- {t.inputBox.reasoningEffortMediumDescription} -
-
- {context.reasoning_effort === "medium" || !context.reasoning_effort ? ( - - ) : ( -
- )} - - handleReasoningEffortSelect("high")} - > -
-
- {t.inputBox.reasoningEffortHigh} -
-
- {t.inputBox.reasoningEffortHighDescription} -
-
- {context.reasoning_effort === "high" ? ( + {context.mode === "ultra" ? ( ) : (
@@ -693,56 +603,165 @@ export function InputBox({ - )} - - - - - - - {selectedModel?.display_name} - - - - - - - {models.map((m) => ( - handleModelSelect(m.name)} - > - {m.display_name} - {m.name === context.model_name ? ( - - ) : ( -
- )} - - ))} - - - - - - - {isNewThread && searchParams.get("mode") !== "skill" && ( -
- -
- )} - {!isNewThread && ( -
- )} + {supportReasoningEffort && context.mode !== "flash" && ( + + +
+ {t.inputBox.reasoningEffort}: + {context.reasoning_effort === "minimal" && " " + t.inputBox.reasoningEffortMinimal} + {context.reasoning_effort === "low" && " " + t.inputBox.reasoningEffortLow} + {context.reasoning_effort === "medium" && " " + t.inputBox.reasoningEffortMedium} + {context.reasoning_effort === "high" && " " + t.inputBox.reasoningEffortHigh} +
+
+ + + + {t.inputBox.reasoningEffort} + + + handleReasoningEffortSelect("minimal")} + > +
+
+ {t.inputBox.reasoningEffortMinimal} +
+
+ {t.inputBox.reasoningEffortMinimalDescription} +
+
+ {context.reasoning_effort === "minimal" ? ( + + ) : ( +
+ )} + + handleReasoningEffortSelect("low")} + > +
+
+ {t.inputBox.reasoningEffortLow} +
+
+ {t.inputBox.reasoningEffortLowDescription} +
+
+ {context.reasoning_effort === "low" ? ( + + ) : ( +
+ )} + + handleReasoningEffortSelect("medium")} + > +
+
+ {t.inputBox.reasoningEffortMedium} +
+
+ {t.inputBox.reasoningEffortMediumDescription} +
+
+ {context.reasoning_effort === "medium" || !context.reasoning_effort ? ( + + ) : ( +
+ )} + + handleReasoningEffortSelect("high")} + > +
+
+ {t.inputBox.reasoningEffortHigh} +
+
+ {t.inputBox.reasoningEffortHighDescription} +
+
+ {context.reasoning_effort === "high" ? ( + + ) : ( +
+ )} + + + + + + )} + + + + + + + {selectedModel?.display_name} + + + + + + + {models.map((m) => ( + handleModelSelect(m.name)} + > + {m.display_name} + {m.name === context.model_name ? ( + + ) : ( +
+ )} + + ))} + + + + + + + {isNewThread && searchParams.get("mode") !== "skill" && ( +
+ +
+ )} + {!isNewThread && ( +
+ )} {!disabled && diff --git a/frontend/src/components/workspace/messages/message-list.tsx b/frontend/src/components/workspace/messages/message-list.tsx index 60593250..f92ce518 100644 --- a/frontend/src/components/workspace/messages/message-list.tsx +++ b/frontend/src/components/workspace/messages/message-list.tsx @@ -199,7 +199,6 @@ export function MessageList({ ); })} {thread.isLoading && } -
); diff --git a/frontend/src/styles/globals.css b/frontend/src/styles/globals.css index bbe79d43..6d2e07d7 100644 --- a/frontend/src/styles/globals.css +++ b/frontend/src/styles/globals.css @@ -224,7 +224,7 @@ :root { --radius: 0.625rem; - --background: oklch(0.9855 0.0098 87.47); + --background: #F9F8FA; --foreground: oklch(0.145 0 0); --card: oklch(1 0.0098 87.47); --card-foreground: oklch(0.145 0 0); @@ -297,7 +297,7 @@ @apply border-border outline-ring/50; } body { - @apply bg-background text-foreground; + @apply text-foreground; } .container-md { diff --git a/scripts/cleanup-containers.sh b/scripts/cleanup-containers.sh old mode 100755 new mode 100644 diff --git a/scripts/docker.sh b/scripts/docker.sh old mode 100755 new mode 100644 diff --git a/skills/public/find-skills/scripts/install-skill.sh b/skills/public/find-skills/scripts/install-skill.sh old mode 100755 new mode 100644 diff --git a/skills/public/skill-creator/scripts/init_skill.py b/skills/public/skill-creator/scripts/init_skill.py old mode 100755 new mode 100644 diff --git a/skills/public/skill-creator/scripts/package_skill.py b/skills/public/skill-creator/scripts/package_skill.py old mode 100755 new mode 100644 diff --git a/skills/public/skill-creator/scripts/quick_validate.py b/skills/public/skill-creator/scripts/quick_validate.py old mode 100755 new mode 100644 diff --git a/skills/public/vercel-deploy-claimable/scripts/deploy.sh b/skills/public/vercel-deploy-claimable/scripts/deploy.sh old mode 100755 new mode 100644