Skip to content

Commit 58af133

Browse files
committed
πŸ€– Complete Tooltip migration to shadcn/ui
- Migrated all 26 files with Tooltip usage (~150 instances total) - Pattern: TooltipWrapper + Tooltip β†’ Tooltip + TooltipTrigger + TooltipContent - Prop changes: align/position β†’ side, inline removed - Extracted HelpIndicator to standalone component - Deleted old Tooltip.tsx (255 lines) and Tooltip.stories.tsx - All static checks passing (lint, typecheck, fmt) Files migrated: - Tool components: FileReadToolCall, TodoToolCall, BashToolCall, FileEditToolCall, ProposePlanToolCall - UI components: ChatInput, NewWorkspaceModal, Context1MCheckbox, StatusIndicator, WorkspaceListItem, KebabMenu, MessageWindow - Complex components: TitleBar (4), ThinkingSlider (4), RightSidebar (4), ProjectSidebar (10) - Code review: RefreshButton, HunkViewer, ReviewPanel, DiffRenderer - Misc: VimTextArea, ConsumerBreakdown, ModelDisplay Generated with `cmux`
1 parent f04bc55 commit 58af133

20 files changed

+363
-777
lines changed

β€Ž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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ export const Context1MCheckbox: React.FC<Context1MCheckboxProps> = ({ modelStrin
2828
</label>
2929
<Tooltip>
3030
<TooltipTrigger asChild>
31-
<span className="text-muted flex cursor-help items-center text-[10px] leading-none">?</span>
31+
<span className="text-muted flex cursor-help items-center text-[10px] leading-none">
32+
?
33+
</span>
3234
</TooltipTrigger>
3335
<TooltipContent>
3436
Enable 1M token context window (beta feature for Claude Sonnet 4/4.5)
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: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ export const KebabMenu: React.FC<KebabMenuProps> = ({ items, className }) => {
8989
<>
9090
<div className="relative">
9191
<Tooltip>
92-
<TooltipTrigger asChild>
93-
{button}
94-
</TooltipTrigger>
92+
<TooltipTrigger asChild>{button}</TooltipTrigger>
9593
<TooltipContent>More actions</TooltipContent>
9694
</Tooltip>
9795
</div>

β€Žsrc/components/Messages/ModelDisplay.tsxβ€Ž

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ export const ModelDisplay: React.FC<ModelDisplayProps> = ({ modelString, showToo
6060
<TooltipTrigger asChild data-model-display-tooltip>
6161
{content}
6262
</TooltipTrigger>
63-
<TooltipContent data-model-tooltip-text>
64-
{modelString}
65-
</TooltipContent>
63+
<TooltipContent data-model-tooltip-text>{modelString}</TooltipContent>
6664
</Tooltip>
6765
);
6866
};

β€Žsrc/components/ProjectSidebar.tsxβ€Ž

Lines changed: 65 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -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}

β€Žsrc/components/RightSidebar.tsxβ€Ž

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -224,48 +224,48 @@ const RightSidebarComponent: React.FC<RightSidebarProps> = ({
224224
role="tablist"
225225
aria-label="Metadata views"
226226
>
227-
<TooltipWrapper inline>
228-
<button
229-
className={cn(
230-
"w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200",
231-
selectedTab === "costs"
232-
? "text-white bg-separator border-b-2 border-b-plan-mode"
233-
: "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
234-
)}
235-
onClick={() => setSelectedTab("costs")}
236-
id={costsTabId}
237-
role="tab"
238-
type="button"
239-
aria-selected={selectedTab === "costs"}
240-
aria-controls={costsPanelId}
241-
>
242-
Costs
243-
</button>
244-
<Tooltip className="tooltip" position="bottom" align="center">
245-
{formatKeybind(KEYBINDS.COSTS_TAB)}
246-
</Tooltip>
247-
</TooltipWrapper>
248-
<TooltipWrapper inline>
249-
<button
250-
className={cn(
251-
"w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200",
252-
selectedTab === "review"
253-
? "text-white bg-separator border-b-2 border-b-plan-mode"
254-
: "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
255-
)}
256-
onClick={() => setSelectedTab("review")}
257-
id={reviewTabId}
258-
role="tab"
259-
type="button"
260-
aria-selected={selectedTab === "review"}
261-
aria-controls={reviewPanelId}
262-
>
263-
Review
264-
</button>
265-
<Tooltip className="tooltip" position="bottom" align="center">
266-
{formatKeybind(KEYBINDS.REVIEW_TAB)}
267-
</Tooltip>
268-
</TooltipWrapper>
227+
<Tooltip>
228+
<TooltipTrigger asChild>
229+
<button
230+
className={cn(
231+
"w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200",
232+
selectedTab === "costs"
233+
? "text-white bg-separator border-b-2 border-b-plan-mode"
234+
: "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
235+
)}
236+
onClick={() => setSelectedTab("costs")}
237+
id={costsTabId}
238+
role="tab"
239+
type="button"
240+
aria-selected={selectedTab === "costs"}
241+
aria-controls={costsPanelId}
242+
>
243+
Costs
244+
</button>
245+
</TooltipTrigger>
246+
<TooltipContent side="bottom">{formatKeybind(KEYBINDS.COSTS_TAB)}</TooltipContent>
247+
</Tooltip>
248+
<Tooltip>
249+
<TooltipTrigger asChild>
250+
<button
251+
className={cn(
252+
"w-full py-2.5 px-[15px] border-none border-solid cursor-pointer font-primary text-[13px] font-medium transition-all duration-200",
253+
selectedTab === "review"
254+
? "text-white bg-separator border-b-2 border-b-plan-mode"
255+
: "bg-transparent text-secondary border-b-2 border-b-transparent hover:bg-background-secondary hover:text-foreground"
256+
)}
257+
onClick={() => setSelectedTab("review")}
258+
id={reviewTabId}
259+
role="tab"
260+
type="button"
261+
aria-selected={selectedTab === "review"}
262+
aria-controls={reviewPanelId}
263+
>
264+
Review
265+
</button>
266+
</TooltipTrigger>
267+
<TooltipContent side="bottom">{formatKeybind(KEYBINDS.REVIEW_TAB)}</TooltipContent>
268+
</Tooltip>
269269
</div>
270270
<div
271271
className={cn("flex-1 overflow-y-auto", selectedTab === "review" ? "p-0" : "p-[15px]")}

β€Žsrc/components/RightSidebar/CodeReview/HunkViewer.tsxβ€Ž

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
type SearchHighlightConfig,
1010
highlightSearchInText,
1111
} from "@/utils/highlighting/highlightSearchTerms";
12-
import { Tooltip, TooltipWrapper } from "../../Tooltip";
12+
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
1313
import { usePersistedState } from "@/hooks/usePersistedState";
1414
import { getReviewExpandStateKey } from "@/constants/storage";
1515
import { KEYBINDS, formatKeybind } from "@/utils/ui/keybinds";
@@ -150,17 +150,17 @@ export const HunkViewer = React.memo<HunkViewerProps>(
150150
>
151151
<div className="bg-separator border-border-light font-monospace flex items-center justify-between gap-2 border-b px-3 py-2 text-xs">
152152
{isRead && (
153-
<TooltipWrapper inline>
154-
<span
155-
className="text-read mr-1 inline-flex items-center text-sm"
156-
aria-label="Marked as read"
157-
>
158-
βœ“
159-
</span>
160-
<Tooltip align="center" position="top">
161-
Marked as read
162-
</Tooltip>
163-
</TooltipWrapper>
153+
<Tooltip>
154+
<TooltipTrigger asChild>
155+
<span
156+
className="text-read mr-1 inline-flex items-center text-sm"
157+
aria-label="Marked as read"
158+
>
159+
βœ“
160+
</span>
161+
</TooltipTrigger>
162+
<TooltipContent side="top">Marked as read</TooltipContent>
163+
</Tooltip>
164164
)}
165165
<div
166166
className="text-foreground min-w-0 truncate font-medium"
@@ -177,19 +177,21 @@ export const HunkViewer = React.memo<HunkViewerProps>(
177177
({lineCount} {lineCount === 1 ? "line" : "lines"})
178178
</span>
179179
{onToggleRead && (
180-
<TooltipWrapper inline>
181-
<button
182-
className="border-border-light text-muted hover:border-read hover:text-read flex cursor-pointer items-center gap-1 rounded-[3px] border bg-transparent px-1.5 py-0.5 text-[11px] transition-all duration-200 hover:bg-white/5 active:scale-95"
183-
data-hunk-id={hunkId}
184-
onClick={handleToggleRead}
185-
aria-label={`Mark as read (${formatKeybind(KEYBINDS.TOGGLE_HUNK_READ)})`}
186-
>
187-
{isRead ? "β—‹" : "β—‰"}
188-
</button>
189-
<Tooltip align="right" position="top">
180+
<Tooltip>
181+
<TooltipTrigger asChild>
182+
<button
183+
className="border-border-light text-muted hover:border-read hover:text-read flex cursor-pointer items-center gap-1 rounded-[3px] border bg-transparent px-1.5 py-0.5 text-[11px] transition-all duration-200 hover:bg-white/5 active:scale-95"
184+
data-hunk-id={hunkId}
185+
onClick={handleToggleRead}
186+
aria-label={`Mark as read (${formatKeybind(KEYBINDS.TOGGLE_HUNK_READ)})`}
187+
>
188+
{isRead ? "β—‹" : "β—‰"}
189+
</button>
190+
</TooltipTrigger>
191+
<TooltipContent side="top" align="end">
190192
Mark as read ({formatKeybind(KEYBINDS.TOGGLE_HUNK_READ)})
191-
</Tooltip>
192-
</TooltipWrapper>
193+
</TooltipContent>
194+
</Tooltip>
193195
)}
194196
</div>
195197
</div>

0 commit comments

Comments
Β (0)