diff --git a/frontend/src/components/bookmarks/BookmarkRow/BookmarkRow.tsx b/frontend/src/components/bookmarks/BookmarkRow/BookmarkRow.tsx
index 40505b4..38a4bf6 100644
--- a/frontend/src/components/bookmarks/BookmarkRow/BookmarkRow.tsx
+++ b/frontend/src/components/bookmarks/BookmarkRow/BookmarkRow.tsx
@@ -2,6 +2,7 @@ import { BookmarkX, FileDown } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Button } from '@/components/ui/button';
import { Item, ItemActions, ItemContent } from '@/components/ui/item';
+import ActionTooltip from '@/components/common/ActionTooltip/ActionTooltip';
import BookmarkRowSkeleton from '@/components/bookmarks/BookmarkRowSkeleton/BookmarkRowSkeleton';
import TrialBadges from '@/components/bookmarks/TrialBadges/TrialBadges';
import { publicTrialId, trialTitle } from '@/lib/trial';
@@ -58,27 +59,29 @@ function BookmarkRow({
{onExport && (
+
+
+
+ )}
+
- )}
-
+
);
diff --git a/frontend/src/components/bookmarks/BookmarksSheet/BookmarksSheet.tsx b/frontend/src/components/bookmarks/BookmarksSheet/BookmarksSheet.tsx
index 5417fec..b69f6c3 100644
--- a/frontend/src/components/bookmarks/BookmarksSheet/BookmarksSheet.tsx
+++ b/frontend/src/components/bookmarks/BookmarksSheet/BookmarksSheet.tsx
@@ -14,6 +14,7 @@ import {
SheetTitle,
} from '@/components/ui/sheet';
import BookmarkRow from '@/components/bookmarks/BookmarkRow/BookmarkRow';
+import ActionTooltip from '@/components/common/ActionTooltip/ActionTooltip';
import { useCachedTrialTranslations } from '@/hooks/useTrialTranslation';
import { useTrialPdfExport } from '@/hooks/useTrialPdfExport';
import { trialQuery } from '@/services/trials';
@@ -52,12 +53,14 @@ function BookmarksSheet({ open, onOpenChange }: BookmarksSheetProps) {
return (
-
-
-
+
+
+
+
+
diff --git a/frontend/src/components/chat/ChatHeader/ChatHeader.tsx b/frontend/src/components/chat/ChatHeader/ChatHeader.tsx
index d4a6c4d..e7823a9 100644
--- a/frontend/src/components/chat/ChatHeader/ChatHeader.tsx
+++ b/frontend/src/components/chat/ChatHeader/ChatHeader.tsx
@@ -1,6 +1,7 @@
import { SquarePen } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Button } from '@/components/ui/button';
+import ActionTooltip from '@/components/common/ActionTooltip/ActionTooltip';
interface ChatHeaderProps {
onNewConversation: () => void;
@@ -11,15 +12,16 @@ function ChatHeader({ onNewConversation }: ChatHeaderProps) {
return (
);
}
diff --git a/frontend/src/components/chat/MessageFeedback/MessageFeedback.tsx b/frontend/src/components/chat/MessageFeedback/MessageFeedback.tsx
index b6c4972..2ef7725 100644
--- a/frontend/src/components/chat/MessageFeedback/MessageFeedback.tsx
+++ b/frontend/src/components/chat/MessageFeedback/MessageFeedback.tsx
@@ -5,6 +5,7 @@ import { toast } from 'sonner';
import { MessageAction, MessageActions } from '@/components/ai-elements/message';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
+import ActionTooltip from '@/components/common/ActionTooltip/ActionTooltip';
import { Collapsible, CollapsibleContent } from '@/components/ui/collapsible';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
@@ -138,16 +139,18 @@ function MessageFeedback({ sessionId, observationId }: MessageFeedbackProps) {
className="gap-1 py-0.5 pr-1 pl-2 font-mono"
>
{nct}
-
+
+
+
))}
diff --git a/frontend/src/components/chat/SelectedTrials/SelectedTrials.tsx b/frontend/src/components/chat/SelectedTrials/SelectedTrials.tsx
index 748dc61..fdd2b47 100644
--- a/frontend/src/components/chat/SelectedTrials/SelectedTrials.tsx
+++ b/frontend/src/components/chat/SelectedTrials/SelectedTrials.tsx
@@ -2,9 +2,13 @@ import { MapPin, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
+import ActionTooltip from '@/components/common/ActionTooltip/ActionTooltip';
import { trialTitle } from '@/lib/trial';
+import { truncate } from '@/lib/utils';
import type { Trial } from '@/types/trial';
+const TOOLTIP_TITLE_MAX_LENGTH = 40;
+
interface SelectedTrialsProps {
trials: Trial[];
onRemove: (trialRef: string) => void;
@@ -25,16 +29,22 @@ function SelectedTrials({ trials, onRemove }: SelectedTrialsProps) {
{label}
-
+
+
);
})}
diff --git a/frontend/src/components/chat/TrialCitation/TrialCitation.tsx b/frontend/src/components/chat/TrialCitation/TrialCitation.tsx
index 604a0d5..3332631 100644
--- a/frontend/src/components/chat/TrialCitation/TrialCitation.tsx
+++ b/frontend/src/components/chat/TrialCitation/TrialCitation.tsx
@@ -11,17 +11,13 @@ import {
import { useIsMobile } from '@/hooks/useIsMobile';
import { useCachedTrialTranslations } from '@/hooks/useTrialTranslation';
import { trialTitle } from '@/lib/trial';
-import { cn } from '@/lib/utils';
+import { cn, truncate } from '@/lib/utils';
import { trialQuery } from '@/services/trials';
const CITATION_TITLE_MAX_LENGTH = 50;
const COMPACT_TITLE_MAX_LENGTH = 28;
const CITATION_DESCRIPTION_MAX_LENGTH = 200;
-function truncate(text: string, max: number): string {
- return text.length > max ? `${text.slice(0, max).trimEnd()}…` : text;
-}
-
interface TrialCitationProps {
trialRef: string;
onSelect: (trialRef: string) => void;
diff --git a/frontend/src/components/common/ActionTooltip/ActionTooltip.tsx b/frontend/src/components/common/ActionTooltip/ActionTooltip.tsx
new file mode 100644
index 0000000..6b45281
--- /dev/null
+++ b/frontend/src/components/common/ActionTooltip/ActionTooltip.tsx
@@ -0,0 +1,21 @@
+import type { ReactNode } from 'react';
+import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
+
+interface ActionTooltipProps {
+ label: string;
+ children: ReactNode;
+}
+
+/** Hover tooltip for a single action button, which must forward refs and props. */
+function ActionTooltip({ label, children }: ActionTooltipProps) {
+ return (
+
+
+ {children}
+ {label}
+
+
+ );
+}
+
+export default ActionTooltip;
diff --git a/frontend/src/components/layout/AppHeader/AppHeader.tsx b/frontend/src/components/layout/AppHeader/AppHeader.tsx
index 47f11e9..e9bed64 100644
--- a/frontend/src/components/layout/AppHeader/AppHeader.tsx
+++ b/frontend/src/components/layout/AppHeader/AppHeader.tsx
@@ -44,14 +44,14 @@ function AppHeader({ onOpenBookmarks, onStartTour }: AppHeaderProps) {