diff --git a/src/components/shared/CopyText/CopyText.tsx b/src/components/shared/CopyText/CopyText.tsx
new file mode 100644
index 000000000..a36c3285a
--- /dev/null
+++ b/src/components/shared/CopyText/CopyText.tsx
@@ -0,0 +1,93 @@
+import { type MouseEvent, useCallback, useState } from "react";
+
+import { Button } from "@/components/ui/button";
+import { Icon } from "@/components/ui/icon";
+import { InlineStack } from "@/components/ui/layout";
+import { Text } from "@/components/ui/typography";
+import { cn } from "@/lib/utils";
+import { copyToClipboard } from "@/utils/string";
+
+interface CopyTextProps {
+ children: string;
+ className?: string;
+}
+
+export const CopyText = ({ children, className }: CopyTextProps) => {
+ const [isCopied, setIsCopied] = useState(false);
+ const [isHovered, setIsHovered] = useState(false);
+
+ const handleCopy = useCallback(() => {
+ copyToClipboard(children);
+ setIsCopied(true);
+ }, [children]);
+
+ const handleButtonClick = useCallback(
+ (e: MouseEvent) => {
+ e.stopPropagation();
+ handleCopy();
+ },
+ [handleCopy],
+ );
+
+ const handleAnimationEnd = useCallback(() => {
+ setIsCopied(false);
+ }, []);
+
+ return (
+
+ setIsHovered(true)}
+ onMouseLeave={() => setIsHovered(false)}
+ title={children}
+ >
+
+
+ {children}
+
+
+
+
+
+
+ );
+};
diff --git a/src/styles/global.css b/src/styles/global.css
index b9ff3d49c..4dd7dec3d 100644
--- a/src/styles/global.css
+++ b/src/styles/global.css
@@ -144,6 +144,21 @@ code {
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
+
+ /* Custom animations */
+ --animate-revert-copied: revert-copied 0.5s ease-in-out forwards;
+
+ @keyframes revert-copied {
+ 0%,
+ 80% {
+ opacity: 1;
+ transform: rotate(0deg) scale(1);
+ }
+ 100% {
+ opacity: 0;
+ transform: rotate(-90deg) scale(0);
+ }
+ }
}
@layer base {