= ({
} relative flex items-center gap-x-2 py-1 px-2 rounded-md`}
>
-
-
-
-
- {assistant.name}
-
-
- {isNameTruncated && (
- {assistant.name}
- )}
-
-
-
- {assistant.name}
-
+
+
diff --git a/web/src/components/ui/truncatedText.tsx b/web/src/components/ui/truncatedText.tsx
new file mode 100644
index 000000000..cd339600b
--- /dev/null
+++ b/web/src/components/ui/truncatedText.tsx
@@ -0,0 +1,86 @@
+import React, {
+ useState,
+ useRef,
+ useLayoutEffect,
+ HTMLAttributes,
+} from "react";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
+
+interface TruncatedTextProps extends HTMLAttributes {
+ text: string;
+ tooltipClassName?: string;
+ tooltipSide?: "top" | "right" | "bottom" | "left";
+ tooltipSideOffset?: number;
+}
+
+/**
+ * Renders passed in text on a single line. If text is truncated,
+ * shows a tooltip on hover with the full text.
+ */
+export function TruncatedText({
+ text,
+ tooltipClassName,
+ tooltipSide = "right",
+ tooltipSideOffset = 5,
+ className = "",
+ ...rest
+}: TruncatedTextProps) {
+ const [isTruncated, setIsTruncated] = useState(false);
+ const visibleRef = useRef(null);
+ const hiddenRef = useRef(null);
+
+ useLayoutEffect(() => {
+ function checkTruncation() {
+ if (visibleRef.current && hiddenRef.current) {
+ const visibleWidth = visibleRef.current.offsetWidth;
+ const fullTextWidth = hiddenRef.current.offsetWidth;
+ setIsTruncated(fullTextWidth > visibleWidth);
+ }
+ }
+
+ checkTruncation();
+ window.addEventListener("resize", checkTruncation);
+ return () => window.removeEventListener("resize", checkTruncation);
+ }, [text]);
+
+ return (
+
+
+
+
+ {text}
+
+
+ {/* Hide offscreen to measure full text width */}
+
+ {text}
+
+ {isTruncated && (
+
+
+ {text}
+
+
+ )}
+
+
+ );
+}