From c1a2ccc0a7b587ddacf09a26b04e32cc632d4a05 Mon Sep 17 00:00:00 2001 From: 777genius Date: Sun, 17 May 2026 01:07:38 +0300 Subject: [PATCH] fix(ui): restore safe model tooltips --- .../team/dialogs/TeamModelSelector.tsx | 32 ++++++++ .../team/members/MemberDraftRow.tsx | 79 ++++++++++++------ src/renderer/components/ui/hover-tooltip.tsx | 81 +++++++++++++++++++ 3 files changed, 167 insertions(+), 25 deletions(-) create mode 100644 src/renderer/components/ui/hover-tooltip.tsx diff --git a/src/renderer/components/team/dialogs/TeamModelSelector.tsx b/src/renderer/components/team/dialogs/TeamModelSelector.tsx index 112d1cfd..5bd21a24 100644 --- a/src/renderer/components/team/dialogs/TeamModelSelector.tsx +++ b/src/renderer/components/team/dialogs/TeamModelSelector.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { ProviderBrandLogo } from '@renderer/components/common/ProviderBrandLogo'; import { Checkbox } from '@renderer/components/ui/checkbox'; +import { HoverTooltip } from '@renderer/components/ui/hover-tooltip'; import { Input } from '@renderer/components/ui/input'; import { Label } from '@renderer/components/ui/label'; import { Popover, PopoverContent, PopoverTrigger } from '@renderer/components/ui/popover'; @@ -47,6 +48,7 @@ import { CheckCircle2, ChevronDown, Filter, + Info, Search, Star, } from 'lucide-react'; @@ -819,6 +821,18 @@ export const TeamModelSelector: React.FC = ({ {modelRecommendation.label} ) : null} + {opt.value === '' ? ( + + + + + + ) : null} {hasModelIssue && ( = ({ > {modelUnavailableReason ? 'Unavailable' : 'Issue'} + {modelStatusMessage ? ( + + + + ) : null} )} {!hasModelIssue && modelDisabledReason && ( @@ -834,6 +858,14 @@ export const TeamModelSelector: React.FC = ({ title={modelDisabledReason} > {TEAM_MODEL_UI_DISABLED_BADGE_LABEL} + + + )} diff --git a/src/renderer/components/team/members/MemberDraftRow.tsx b/src/renderer/components/team/members/MemberDraftRow.tsx index 3c0b9035..eb2a802f 100644 --- a/src/renderer/components/team/members/MemberDraftRow.tsx +++ b/src/renderer/components/team/members/MemberDraftRow.tsx @@ -12,6 +12,7 @@ import { import { RoleSelect } from '@renderer/components/team/RoleSelect'; import { Button } from '@renderer/components/ui/button'; import { Checkbox } from '@renderer/components/ui/checkbox'; +import { HoverTooltip } from '@renderer/components/ui/hover-tooltip'; import { Input } from '@renderer/components/ui/input'; import { Label } from '@renderer/components/ui/label'; import { MentionableTextarea } from '@renderer/components/ui/MentionableTextarea'; @@ -262,6 +263,21 @@ export const MemberDraftRow = ({ const modelHelpDescriptionId = modelTooltipText ? `member-${member.id}-model-help` : undefined; const modelButtonDescribedBy = [modelIssueDescriptionId, modelHelpDescriptionId].filter(Boolean).join(' ') || undefined; + const modelButtonTooltipContent = + currentModelIssueText || modelTooltipText ? ( + <> + {currentModelIssueText ? ( + {currentModelIssueText} + ) : null} + {modelTooltipText ? ( + + {modelTooltipText} + + ) : null} + + ) : null; const hasCustomProviderOrModel = !forceInheritedModelSettings && Boolean(member.providerId || member.model?.trim()); const showSonnetExtraUsageWarning = @@ -357,7 +373,13 @@ export const MemberDraftRow = ({ ) : null}
- + - + {modelTooltipText ? ( {modelTooltipText} @@ -400,34 +422,41 @@ export const MemberDraftRow = ({
{showWorktreeIsolationControls ? (
-
- - onWorktreeIsolationChange?.(member.id, checked === true) - } - /> - -
+ + onWorktreeIsolationChange?.(member.id, checked === true) + } + /> + +
+ {worktreeIsolationDescription} diff --git a/src/renderer/components/ui/hover-tooltip.tsx b/src/renderer/components/ui/hover-tooltip.tsx new file mode 100644 index 00000000..b9a06c27 --- /dev/null +++ b/src/renderer/components/ui/hover-tooltip.tsx @@ -0,0 +1,81 @@ +import React from 'react'; + +import { cn } from '@renderer/lib/utils'; + +type HoverTooltipSide = 'top' | 'bottom'; +type HoverTooltipAlign = 'start' | 'center' | 'end'; + +interface HoverTooltipProps { + children: React.ReactNode; + content: React.ReactNode; + align?: HoverTooltipAlign; + as?: 'span' | 'div'; + className?: string; + contentClassName?: string; + disabled?: boolean; + side?: HoverTooltipSide; + stopClickPropagation?: boolean; + title?: string; +} + +const sideClassBySide: Record = { + top: 'bottom-full mb-2', + bottom: 'top-full mt-2', +}; + +const alignClassByAlign: Record = { + start: 'left-0', + center: 'left-1/2 -translate-x-1/2', + end: 'right-0', +}; + +const renderTooltipContent = (content: React.ReactNode): React.JSX.Element => { + return typeof content === 'string' ? ( + {content} + ) : ( + {content} + ); +}; + +export const HoverTooltip = ({ + children, + content, + align = 'center', + as = 'span', + className, + contentClassName, + disabled = false, + side = 'top', + stopClickPropagation = false, + title, +}: Readonly): React.JSX.Element => { + const TooltipWrapper = as; + + if (disabled || !content) { + return {children}; + } + + return ( + event.stopPropagation() : undefined + } + > + {children} + + + ); +};