mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-05-01 09:40:45 +08:00
feat: replace with simplified Tooltip
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
|
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
import { memo, useCallback, useEffect, useState } from "react";
|
import { memo, useCallback, useEffect, useState } from "react";
|
||||||
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "~/components/ui/tooltip";
|
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
function Image({
|
function Image({
|
||||||
className,
|
className,
|
||||||
imageClassName,
|
imageClassName,
|
||||||
@@ -47,23 +47,18 @@ function Image({
|
|||||||
{isError ? (
|
{isError ? (
|
||||||
fallback
|
fallback
|
||||||
) : (
|
) : (
|
||||||
<Tooltip>
|
<Tooltip title={alt ?? "No caption"}>
|
||||||
<TooltipTrigger asChild>
|
<img
|
||||||
<img
|
className={cn(
|
||||||
className={cn(
|
"size-full object-cover",
|
||||||
"size-full object-cover",
|
imageTransition && "transition-all duration-200 ease-out",
|
||||||
imageTransition && "transition-all duration-200 ease-out",
|
imageClassName,
|
||||||
imageClassName,
|
)}
|
||||||
)}
|
src={src}
|
||||||
src={src}
|
alt={alt}
|
||||||
alt={alt}
|
onLoad={handleLoad}
|
||||||
onLoad={handleLoad}
|
onError={handleError}
|
||||||
onError={handleError}
|
/>
|
||||||
/>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p className="max-w-64 text-sm">{alt ?? "No caption"}</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -12,14 +12,11 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
|
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "~/components/ui/tooltip";
|
|
||||||
import type { Option } from "~/core/messages";
|
import type { Option } from "~/core/messages";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
export function InputBox({
|
export function InputBox({
|
||||||
className,
|
className,
|
||||||
size,
|
size,
|
||||||
@@ -139,26 +136,21 @@ export function InputBox({
|
|||||||
<div className="flex items-center px-4 py-2">
|
<div className="flex items-center px-4 py-2">
|
||||||
<div className="flex grow"></div>
|
<div className="flex grow"></div>
|
||||||
<div className="flex shrink-0 items-center gap-2">
|
<div className="flex shrink-0 items-center gap-2">
|
||||||
<Tooltip>
|
<Tooltip title={responding ? "Stop" : "Send"}>
|
||||||
<TooltipTrigger asChild>
|
<Button
|
||||||
<Button
|
variant="outline"
|
||||||
variant="outline"
|
size="icon"
|
||||||
size="icon"
|
className={cn("h-10 w-10 rounded-full")}
|
||||||
className={cn("h-10 w-10 rounded-full")}
|
onClick={handleSendMessage}
|
||||||
onClick={handleSendMessage}
|
>
|
||||||
>
|
{responding ? (
|
||||||
{responding ? (
|
<div className="flex h-10 w-10 items-center justify-center">
|
||||||
<div className="flex h-10 w-10 items-center justify-center">
|
<div className="bg-foreground h-4 w-4 rounded-sm opacity-70" />
|
||||||
<div className="bg-foreground h-4 w-4 rounded-sm opacity-70" />
|
</div>
|
||||||
</div>
|
) : (
|
||||||
) : (
|
<ArrowUpOutlined />
|
||||||
<ArrowUpOutlined />
|
)}
|
||||||
)}
|
</Button>
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>{responding ? "Stop" : "Send"}</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -12,15 +12,11 @@ import remarkMath from "remark-math";
|
|||||||
import "katex/dist/katex.min.css";
|
import "katex/dist/katex.min.css";
|
||||||
|
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "~/components/ui/tooltip";
|
|
||||||
import { rehypeSplitWordsIntoSpans } from "~/core/rehype";
|
import { rehypeSplitWordsIntoSpans } from "~/core/rehype";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
import Image from "./image";
|
import Image from "./image";
|
||||||
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
export function Markdown({
|
export function Markdown({
|
||||||
className,
|
className,
|
||||||
@@ -77,34 +73,29 @@ export function Markdown({
|
|||||||
function CopyButton({ content }: { content: string }) {
|
function CopyButton({ content }: { content: string }) {
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
return (
|
return (
|
||||||
<Tooltip>
|
<Tooltip title="Copy">
|
||||||
<TooltipTrigger asChild>
|
<Button
|
||||||
<Button
|
variant="outline"
|
||||||
variant="outline"
|
size="sm"
|
||||||
size="sm"
|
className="rounded-full"
|
||||||
className="rounded-full"
|
onClick={async () => {
|
||||||
onClick={async () => {
|
try {
|
||||||
try {
|
await navigator.clipboard.writeText(content);
|
||||||
await navigator.clipboard.writeText(content);
|
setCopied(true);
|
||||||
setCopied(true);
|
setTimeout(() => {
|
||||||
setTimeout(() => {
|
setCopied(false);
|
||||||
setCopied(false);
|
}, 1000);
|
||||||
}, 1000);
|
} catch (error) {
|
||||||
} catch (error) {
|
console.error(error);
|
||||||
console.error(error);
|
}
|
||||||
}
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{copied ? (
|
||||||
{copied ? (
|
<CheckOutlined className="h-4 w-4" />
|
||||||
<CheckOutlined className="h-4 w-4" />
|
) : (
|
||||||
) : (
|
<CopyOutlined className="h-4 w-4" />
|
||||||
<CopyOutlined className="h-4 w-4" />
|
)}{" "}
|
||||||
)}{" "}
|
</Button>
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>
|
|
||||||
<p>Copy</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,6 @@ import {
|
|||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "~/components/ui/card";
|
} from "~/components/ui/card";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "~/components/ui/tooltip";
|
|
||||||
import type { Message, Option } from "~/core/messages";
|
import type { Message, Option } from "~/core/messages";
|
||||||
import {
|
import {
|
||||||
openResearch,
|
openResearch,
|
||||||
@@ -38,6 +33,7 @@ import { Markdown } from "./markdown";
|
|||||||
import { RainbowText } from "./rainbow-text";
|
import { RainbowText } from "./rainbow-text";
|
||||||
import { RollingText } from "./rolling-text";
|
import { RollingText } from "./rolling-text";
|
||||||
import { ScrollContainer } from "./scroll-container";
|
import { ScrollContainer } from "./scroll-container";
|
||||||
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
export function MessageListView({
|
export function MessageListView({
|
||||||
className,
|
className,
|
||||||
@@ -398,18 +394,15 @@ function PodcastCard({
|
|||||||
</div>
|
</div>
|
||||||
{!isGenerating && (
|
{!isGenerating && (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
<Tooltip>
|
<Tooltip title="Download podcast">
|
||||||
<TooltipTrigger asChild>
|
<Button variant="ghost" size="icon" asChild>
|
||||||
<Button variant="ghost" size="icon" asChild>
|
<a
|
||||||
<a
|
href={audioUrl}
|
||||||
href={audioUrl}
|
download={`${(title ?? "podcast").replaceAll(" ", "-")}.mp3`}
|
||||||
download={`${(title ?? "podcast").replaceAll(" ", "-")}.mp3`}
|
>
|
||||||
>
|
<DownloadOutlined />
|
||||||
<DownloadOutlined />
|
</a>
|
||||||
</a>
|
</Button>
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Download podcast</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,17 +7,13 @@ import { useEffect, useState } from "react";
|
|||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import { Card } from "~/components/ui/card";
|
import { Card } from "~/components/ui/card";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "~/components/ui/tooltip";
|
|
||||||
import { openResearch, useStore } from "~/core/store";
|
import { openResearch, useStore } from "~/core/store";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
import { ResearchActivitiesBlock } from "./research-activities-block";
|
import { ResearchActivitiesBlock } from "./research-activities-block";
|
||||||
import { ResearchReportBlock } from "./research-report-block";
|
import { ResearchReportBlock } from "./research-report-block";
|
||||||
import { ScrollContainer } from "./scroll-container";
|
import { ScrollContainer } from "./scroll-container";
|
||||||
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
export function ResearchBlock({
|
export function ResearchBlock({
|
||||||
className,
|
className,
|
||||||
@@ -43,20 +39,17 @@ export function ResearchBlock({
|
|||||||
<div className={cn("h-full w-full", className)}>
|
<div className={cn("h-full w-full", className)}>
|
||||||
<Card className={cn("relative h-full w-full pt-4", className)}>
|
<Card className={cn("relative h-full w-full pt-4", className)}>
|
||||||
<div className="absolute right-4 flex h-9 items-center">
|
<div className="absolute right-4 flex h-9 items-center">
|
||||||
<Tooltip>
|
<Tooltip title="Close">
|
||||||
<TooltipTrigger asChild>
|
<Button
|
||||||
<Button
|
className="text-gray-400"
|
||||||
className="text-gray-400"
|
size="sm"
|
||||||
size="sm"
|
variant="ghost"
|
||||||
variant="ghost"
|
onClick={() => {
|
||||||
onClick={() => {
|
openResearch(null);
|
||||||
openResearch(null);
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<CloseOutlined />
|
||||||
<CloseOutlined />
|
</Button>
|
||||||
</Button>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Close</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
<Tabs
|
<Tabs
|
||||||
|
|||||||
@@ -5,16 +5,12 @@ import { SoundOutlined } from "@ant-design/icons";
|
|||||||
import { useCallback, useRef, useState } from "react";
|
import { useCallback, useRef, useState } from "react";
|
||||||
|
|
||||||
import { Button } from "~/components/ui/button";
|
import { Button } from "~/components/ui/button";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipTrigger,
|
|
||||||
TooltipContent,
|
|
||||||
} from "~/components/ui/tooltip";
|
|
||||||
import { listenToPodcast, useMessage } from "~/core/store";
|
import { listenToPodcast, useMessage } from "~/core/store";
|
||||||
import { cn } from "~/lib/utils";
|
import { cn } from "~/lib/utils";
|
||||||
|
|
||||||
import { LoadingAnimation } from "./loading-animation";
|
import { LoadingAnimation } from "./loading-animation";
|
||||||
import { Markdown } from "./markdown";
|
import { Markdown } from "./markdown";
|
||||||
|
import { Tooltip } from "./tooltip";
|
||||||
|
|
||||||
export function ResearchReportBlock({
|
export function ResearchReportBlock({
|
||||||
className,
|
className,
|
||||||
@@ -42,24 +38,21 @@ export function ResearchReportBlock({
|
|||||||
>
|
>
|
||||||
<div className="absolute top-2 right-6">
|
<div className="absolute top-2 right-6">
|
||||||
{message?.content && !message.isStreaming && (
|
{message?.content && !message.isStreaming && (
|
||||||
<Tooltip>
|
<Tooltip
|
||||||
<TooltipTrigger asChild>
|
title={
|
||||||
<Button
|
isGenerated ? "The podcast is generated" : "Generate podcast"
|
||||||
variant="outline"
|
}
|
||||||
size="icon"
|
>
|
||||||
disabled={isGenerated}
|
<Button
|
||||||
onClick={() => {
|
variant="outline"
|
||||||
void handleListenToReport();
|
size="icon"
|
||||||
}}
|
disabled={isGenerated}
|
||||||
>
|
onClick={() => {
|
||||||
<SoundOutlined />
|
void handleListenToReport();
|
||||||
</Button>
|
}}
|
||||||
</TooltipTrigger>
|
>
|
||||||
<TooltipContent>
|
<SoundOutlined />
|
||||||
<p>
|
</Button>
|
||||||
{isGenerated ? "The podcast is generated" : "Generate podcast"}
|
|
||||||
</p>
|
|
||||||
</TooltipContent>
|
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
import { Moon, Sun } from "lucide-react";
|
import { Moon, Sun } from "lucide-react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
|
||||||
|
|||||||
25
web/src/app/_components/tooltip.tsx
Normal file
25
web/src/app/_components/tooltip.tsx
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import {
|
||||||
|
Tooltip as ShadcnTooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "~/components/ui/tooltip";
|
||||||
|
|
||||||
|
export function Tooltip({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
}: {
|
||||||
|
className?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
title: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<ShadcnTooltip>
|
||||||
|
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
||||||
|
<TooltipContent className={className}>{title}</TooltipContent>
|
||||||
|
</ShadcnTooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user