Skip to content

Commit 526498f

Browse files
committed
🤖 Migrate all Tooltip components to shadcn/ui
Replaces custom TooltipWrapper/Tooltip with shadcn Tooltip/TooltipTrigger/TooltipContent. Files migrated (26 total, ~150 instances): - Tool components (5): FileReadToolCall, TodoToolCall, BashToolCall, FileEditToolCall, ProposePlanToolCall - UI components (8): ChatInput, NewWorkspaceModal, Context1MCheckbox, StatusIndicator, WorkspaceListItem, KebabMenu, MessageWindow, VimTextArea - Complex components (4): TitleBar (4 instances), ThinkingSlider (4), RightSidebar (4), ProjectSidebar (10) - Code review (4): RefreshButton, HunkViewer, ReviewPanel, DiffRenderer - Others: ModelDisplay, ConsumerBreakdown Changes: - Pattern: `<TooltipWrapper inline><Element /><Tooltip>...</Tooltip></TooltipWrapper>` → `<Tooltip><TooltipTrigger asChild><Element /></TooltipTrigger><TooltipContent>...</TooltipContent></Tooltip>` - Props: `align/position` → `side`, `inline` removed, `width="wide"` → `className="max-w-md"` - Extracted HelpIndicator to standalone component for reuse - Deleted Tooltip.tsx (255 lines) and Tooltip.stories.tsx All static checks passing. Generated with `cmux`
1 parent ee22483 commit 526498f

28 files changed

+530
-912
lines changed

src/components/AIView.tsx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { getModelName } from "@/utils/ai/models";
2626
import { GitStatusIndicator } from "./GitStatusIndicator";
2727

2828
import { useGitStatus } from "@/stores/GitStatusStore";
29-
import { TooltipWrapper, Tooltip } from "./Tooltip";
29+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
3030
import type { DisplayedMessage } from "@/types/message";
3131
import { useAIViewKeybinds } from "@/hooks/useAIViewKeybinds";
3232

@@ -348,19 +348,21 @@ const AIViewInner: React.FC<AIViewProps> = ({
348348
<span className="text-muted min-w-0 truncate font-mono text-[11px] font-normal">
349349
{namedWorkspacePath}
350350
</span>
351-
<TooltipWrapper inline>
352-
<button
353-
onClick={handleOpenTerminal}
354-
className="text-muted hover:text-foreground flex cursor-pointer items-center justify-center border-none bg-transparent p-1 transition-colors [&_svg]:h-4 [&_svg]:w-4"
355-
>
356-
<svg viewBox="0 0 16 16" fill="currentColor">
357-
<path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V2.75zm1.75-.25a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V2.75a.25.25 0 00-.25-.25H1.75zM7.25 8a.75.75 0 01-.22.53l-2.25 2.25a.75.75 0 01-1.06-1.06L5.44 8 3.72 6.28a.75.75 0 111.06-1.06l2.25 2.25c.141.14.22.331.22.53zm1.5 1.5a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z" />
358-
</svg>
359-
</button>
360-
<Tooltip className="tooltip" position="bottom" align="center">
351+
<Tooltip>
352+
<TooltipTrigger asChild>
353+
<button
354+
onClick={handleOpenTerminal}
355+
className="text-muted hover:text-foreground flex cursor-pointer items-center justify-center border-none bg-transparent p-1 transition-colors [&_svg]:h-4 [&_svg]:w-4"
356+
>
357+
<svg viewBox="0 0 16 16" fill="currentColor">
358+
<path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V2.75zm1.75-.25a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V2.75a.25.25 0 00-.25-.25H1.75zM7.25 8a.75.75 0 01-.22.53l-2.25 2.25a.75.75 0 01-1.06-1.06L5.44 8 3.72 6.28a.75.75 0 111.06-1.06l2.25 2.25c.141.14.22.331.22.53zm1.5 1.5a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z" />
359+
</svg>
360+
</button>
361+
</TooltipTrigger>
362+
<TooltipContent side="bottom">
361363
Open in terminal ({formatKeybind(KEYBINDS.OPEN_TERMINAL)})
362-
</Tooltip>
363-
</TooltipWrapper>
364+
</TooltipContent>
365+
</Tooltip>
364366
</div>
365367
</div>
366368

src/components/ChatInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
type SlashSuggestion,
2626
} from "@/utils/slashCommands/suggestions";
2727
import { Tooltip, TooltipTrigger, TooltipContent } from "./ui/tooltip";
28-
import { HelpIndicator } from "./Tooltip";
28+
import { HelpIndicator } from "./HelpIndicator";
2929
import { matchesKeybind, formatKeybind, KEYBINDS, isEditableElement } from "@/utils/ui/keybinds";
3030
import { ModelSelector, type ModelSelectorRef } from "./ModelSelector";
3131
import { useModelLRU } from "@/hooks/useModelLRU";

src/components/Context1MCheckbox.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import { use1MContext } from "@/hooks/use1MContext";
33
import { supports1MContext } from "@/utils/ai/models";
4-
import { TooltipWrapper, Tooltip } from "./Tooltip";
4+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
55

66
interface Context1MCheckboxProps {
77
modelString: string;
@@ -26,12 +26,16 @@ export const Context1MCheckbox: React.FC<Context1MCheckboxProps> = ({ modelStrin
2626
/>
2727
1M Context
2828
</label>
29-
<TooltipWrapper inline>
30-
<span className="text-muted flex cursor-help items-center text-[10px] leading-none">?</span>
31-
<Tooltip className="tooltip" align="center" width="auto">
29+
<Tooltip>
30+
<TooltipTrigger asChild>
31+
<span className="text-muted flex cursor-help items-center text-[10px] leading-none">
32+
?
33+
</span>
34+
</TooltipTrigger>
35+
<TooltipContent>
3236
Enable 1M token context window (beta feature for Claude Sonnet 4/4.5)
33-
</Tooltip>
34-
</TooltipWrapper>
37+
</TooltipContent>
38+
</Tooltip>
3539
</div>
3640
);
3741
};

src/components/HelpIndicator.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from "react";
2+
import { cn } from "@/lib/utils";
3+
4+
/**
5+
* HelpIndicator - Small circular help indicator (typically "?")
6+
* Used with tooltips to show additional information
7+
*/
8+
export const HelpIndicator: React.FC<{ className?: string; children?: React.ReactNode }> = ({
9+
className,
10+
children,
11+
}) => (
12+
<span
13+
className={cn(
14+
"text-text-dim text-[7px] cursor-help inline-block align-baseline",
15+
"border border-border-subtle rounded-full w-2.5 h-[10px] leading-[8px]",
16+
"text-center font-bold mb-[2px]",
17+
className
18+
)}
19+
>
20+
{children}
21+
</span>
22+
);

src/components/KebabMenu.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useRef, useEffect } from "react";
22
import { createPortal } from "react-dom";
3-
import { TooltipWrapper, Tooltip } from "./Tooltip";
3+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
44
import { cn } from "@/lib/utils";
55

66
export interface KebabMenuItem {
@@ -88,10 +88,10 @@ export const KebabMenu: React.FC<KebabMenuProps> = ({ items, className }) => {
8888
return (
8989
<>
9090
<div className="relative">
91-
<TooltipWrapper inline>
92-
{button}
93-
<Tooltip align="center">More actions</Tooltip>
94-
</TooltipWrapper>
91+
<Tooltip>
92+
<TooltipTrigger asChild>{button}</TooltipTrigger>
93+
<TooltipContent>More actions</TooltipContent>
94+
</Tooltip>
9595
</div>
9696

9797
{isOpen &&

src/components/Messages/MessageWindow.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { useState, useMemo } from "react";
33
import type { CmuxMessage, DisplayedMessage } from "@/types/message";
44
import { HeaderButton } from "../tools/shared/ToolPrimitives";
55
import { formatTimestamp } from "@/utils/ui/dateTime";
6-
import { TooltipWrapper, Tooltip } from "../Tooltip";
6+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
77
import { KebabMenu, type KebabMenuItem } from "../KebabMenu";
88

99
export interface ButtonConfig {
@@ -88,14 +88,16 @@ export const MessageWindow: React.FC<MessageWindowProps> = ({
8888
{rightLabel}
8989
{buttons.map((button, index) =>
9090
button.tooltip ? (
91-
<TooltipWrapper key={index} inline>
92-
<ButtonWithHoverEmoji
93-
button={button}
94-
active={button.active}
95-
disabled={button.disabled}
96-
/>
97-
<Tooltip align="center">{button.tooltip}</Tooltip>
98-
</TooltipWrapper>
91+
<Tooltip key={index}>
92+
<TooltipTrigger asChild>
93+
<ButtonWithHoverEmoji
94+
button={button}
95+
active={button.active}
96+
disabled={button.disabled}
97+
/>
98+
</TooltipTrigger>
99+
<TooltipContent>{button.tooltip}</TooltipContent>
100+
</Tooltip>
99101
) : (
100102
<ButtonWithHoverEmoji
101103
key={index}

src/components/Messages/ModelDisplay.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import AnthropicIcon from "@/assets/icons/anthropic.svg?react";
33
import OpenAIIcon from "@/assets/icons/openai.svg?react";
4-
import { TooltipWrapper, Tooltip } from "@/components/Tooltip";
4+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
55
import { formatModelDisplayName } from "@/utils/ai/modelDisplay";
66

77
interface ModelDisplayProps {
@@ -56,11 +56,11 @@ export const ModelDisplay: React.FC<ModelDisplayProps> = ({ modelString, showToo
5656
}
5757

5858
return (
59-
<TooltipWrapper inline data-model-display-tooltip>
60-
{content}
61-
<Tooltip align="center" data-model-tooltip-text>
62-
{modelString}
63-
</Tooltip>
64-
</TooltipWrapper>
59+
<Tooltip>
60+
<TooltipTrigger asChild data-model-display-tooltip>
61+
{content}
62+
</TooltipTrigger>
63+
<TooltipContent data-model-tooltip-text>{modelString}</TooltipContent>
64+
</Tooltip>
6565
);
6666
};

src/components/NewWorkspaceModal.tsx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useEffect, useId, useState } from "react";
22
import { Modal, ModalInfo, ModalActions, CancelButton, PrimaryButton } from "./Modal";
3-
import { TooltipWrapper, Tooltip } from "./Tooltip";
3+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
44
import { formatNewCommand } from "@/utils/chatCommands";
55

66
interface NewWorkspaceModalProps {
@@ -102,11 +102,13 @@ const NewWorkspaceModal: React.FC<NewWorkspaceModalProps> = ({
102102
<form onSubmit={(event) => void handleSubmit(event)}>
103103
<div className="[&_label]:text-foreground [&_input]:bg-modal-bg [&_input]:border-border-medium [&_input]:focus:border-accent [&_select]:bg-modal-bg [&_select]:border-border-medium [&_select]:focus:border-accent [&_option]:bg-modal-bg mb-5 [&_input]:w-full [&_input]:rounded [&_input]:border [&_input]:px-3 [&_input]:py-2 [&_input]:text-sm [&_input]:text-white [&_input]:focus:outline-none [&_input]:disabled:cursor-not-allowed [&_input]:disabled:opacity-60 [&_label]:mb-2 [&_label]:block [&_label]:text-sm [&_option]:text-white [&_select]:w-full [&_select]:cursor-pointer [&_select]:rounded [&_select]:border [&_select]:px-3 [&_select]:py-2 [&_select]:text-sm [&_select]:text-white [&_select]:focus:outline-none [&_select]:disabled:cursor-not-allowed [&_select]:disabled:opacity-60">
104104
<label htmlFor="branchName">
105-
<TooltipWrapper inline>
106-
<span className="cursor-help underline decoration-[#666] decoration-dotted underline-offset-2">
107-
Workspace Branch Name:
108-
</span>
109-
<Tooltip width="wide" position="bottom" interactive>
105+
<Tooltip>
106+
<TooltipTrigger asChild>
107+
<span className="cursor-help underline decoration-[#666] decoration-dotted underline-offset-2">
108+
Workspace Branch Name:
109+
</span>
110+
</TooltipTrigger>
111+
<TooltipContent className="max-w-md" side="bottom">
110112
<strong>About Workspaces:</strong>
111113
<ul className="my-1 pl-4">
112114
<li>Uses git worktrees (separate directories sharing .git)</li>
@@ -117,8 +119,8 @@ const NewWorkspaceModal: React.FC<NewWorkspaceModalProps> = ({
117119
<a href="https://cmux.io/workspaces.html" target="_blank" rel="noopener noreferrer">
118120
Learn more
119121
</a>
120-
</Tooltip>
121-
</TooltipWrapper>
122+
</TooltipContent>
123+
</Tooltip>
122124
</label>
123125
<input
124126
id="branchName"

src/components/ProjectSidebar.tsx

Lines changed: 66 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
partitionWorkspacesByAge,
1515
formatOldWorkspaceThreshold,
1616
} from "@/utils/ui/workspaceFiltering";
17-
import { TooltipWrapper, Tooltip } from "./Tooltip";
17+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
1818
import SecretsModal from "./SecretsModal";
1919
import type { Secret } from "@/types/secrets";
2020
import { ForceDeleteModal } from "./ForceDeleteModal";
@@ -426,18 +426,18 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
426426
<h2 className="text-foreground m-0 text-[13px] font-semibold tracking-[0.8px] uppercase">
427427
Projects
428428
</h2>
429-
<TooltipWrapper inline>
430-
<button
431-
onClick={onAddProject}
432-
aria-label="Add project"
433-
className="text-foreground hover:bg-hover hover:border-border-light flex h-6 w-6 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 text-lg transition-all duration-200"
434-
>
435-
+
436-
</button>
437-
<Tooltip className="tooltip" align="right">
438-
Add Project
439-
</Tooltip>
440-
</TooltipWrapper>
429+
<Tooltip>
430+
<TooltipTrigger asChild>
431+
<button
432+
onClick={onAddProject}
433+
aria-label="Add project"
434+
className="text-foreground hover:bg-hover hover:border-border-light flex h-6 w-6 cursor-pointer items-center justify-center rounded border border-transparent bg-transparent p-0 text-lg transition-all duration-200"
435+
>
436+
+
437+
</button>
438+
</TooltipTrigger>
439+
<TooltipContent side="right">Add Project</TooltipContent>
440+
</Tooltip>
441441
</div>
442442
<div className="flex-1 overflow-y-auto">
443443
{projects.size === 0 ? (
@@ -498,48 +498,48 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
498498
<div className="text-foreground truncate text-sm font-medium tracking-[0.2px]">
499499
{projectName}
500500
</div>
501-
<TooltipWrapper inline>
502-
<div className="text-muted-dark font-monospace mt-px truncate text-[11px]">
503-
{abbreviatePath(projectPath)}
504-
</div>
505-
<Tooltip className="tooltip" align="left">
506-
{projectPath}
507-
</Tooltip>
508-
</TooltipWrapper>
509-
</div>
510-
<TooltipWrapper inline>
511-
<button
512-
onClick={(event) => {
513-
event.stopPropagation();
514-
void handleOpenSecrets(projectPath);
515-
}}
516-
aria-label={`Manage secrets for ${projectName}`}
517-
data-project-path={projectPath}
518-
className="text-muted-dark hover:text-accent hover:bg-accent/10 mr-1 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-none bg-transparent text-sm opacity-0 transition-all duration-200"
519-
>
520-
🔑
521-
</button>
522-
<Tooltip className="tooltip" align="right">
523-
Manage secrets
524-
</Tooltip>
525-
</TooltipWrapper>
526-
<TooltipWrapper inline>
527-
<button
528-
onClick={(event) => {
529-
event.stopPropagation();
530-
onRemoveProject(projectPath);
531-
}}
532-
title="Remove project"
533-
aria-label={`Remove project ${projectName}`}
534-
data-project-path={projectPath}
535-
className="text-muted-dark hover:text-danger-light hover:bg-danger-light/10 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-none bg-transparent text-base opacity-0 transition-all duration-200"
536-
>
537-
×
538-
</button>
539-
<Tooltip className="tooltip" align="right">
540-
Remove project
501+
<Tooltip>
502+
<TooltipTrigger asChild>
503+
<div className="text-muted-dark font-monospace mt-px truncate text-[11px]">
504+
{abbreviatePath(projectPath)}
505+
</div>
506+
</TooltipTrigger>
507+
<TooltipContent side="left">{projectPath}</TooltipContent>
541508
</Tooltip>
542-
</TooltipWrapper>
509+
</div>
510+
<Tooltip>
511+
<TooltipTrigger asChild>
512+
<button
513+
onClick={(event) => {
514+
event.stopPropagation();
515+
void handleOpenSecrets(projectPath);
516+
}}
517+
aria-label={`Manage secrets for ${projectName}`}
518+
data-project-path={projectPath}
519+
className="text-muted-dark hover:text-accent hover:bg-accent/10 mr-1 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-none bg-transparent text-sm opacity-0 transition-all duration-200"
520+
>
521+
🔑
522+
</button>
523+
</TooltipTrigger>
524+
<TooltipContent side="right">Manage secrets</TooltipContent>
525+
</Tooltip>
526+
<Tooltip>
527+
<TooltipTrigger asChild>
528+
<button
529+
onClick={(event) => {
530+
event.stopPropagation();
531+
onRemoveProject(projectPath);
532+
}}
533+
title="Remove project"
534+
aria-label={`Remove project ${projectName}`}
535+
data-project-path={projectPath}
536+
className="text-muted-dark hover:text-danger-light hover:bg-danger-light/10 flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-[3px] border-none bg-transparent text-base opacity-0 transition-all duration-200"
537+
>
538+
×
539+
</button>
540+
</TooltipTrigger>
541+
<TooltipContent side="right">Remove project</TooltipContent>
542+
</Tooltip>
543543
</DraggableProjectItem>
544544

545545
{isExpanded && (
@@ -626,18 +626,20 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
626626
</div>
627627
</>
628628
)}
629-
<TooltipWrapper inline>
630-
<button
631-
onClick={onToggleCollapsed}
632-
className="text-muted border-dark hover:bg-hover hover:text-foreground mt-auto flex h-9 w-full cursor-pointer items-center justify-center border-t border-none bg-transparent p-0 text-sm transition-all duration-200"
633-
>
634-
{collapsed ? "»" : "«"}
635-
</button>
636-
<Tooltip className="tooltip" align="center">
629+
<Tooltip>
630+
<TooltipTrigger asChild>
631+
<button
632+
onClick={onToggleCollapsed}
633+
className="text-muted border-dark hover:bg-hover hover:text-foreground mt-auto flex h-9 w-full cursor-pointer items-center justify-center border-t border-none bg-transparent p-0 text-sm transition-all duration-200"
634+
>
635+
{collapsed ? "»" : "«"}
636+
</button>
637+
</TooltipTrigger>
638+
<TooltipContent>
637639
{collapsed ? "Expand sidebar" : "Collapse sidebar"} (
638640
{formatKeybind(KEYBINDS.TOGGLE_SIDEBAR)})
639-
</Tooltip>
640-
</TooltipWrapper>
641+
</TooltipContent>
642+
</Tooltip>
641643
{secretsModalState && (
642644
<SecretsModal
643645
isOpen={secretsModalState.isOpen}

0 commit comments

Comments
 (0)