authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-22 12:22:15-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-22 12:22:15-07:00
logaa2b25d29b4b13ef1c2ee80e1afade1635de9ca8
tree77b0f5f98572739766a0bce1f176dd01a100d250
parent7d658c24dbf81d7cfc594b89d9cd5b8bab0e1046
signature Signed by PGP key B5690EEEBB952194

refactor(web): one wrap width for tooltips, plus a code variant (#13031)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

38 files changed, 60 insertions(+), 153 deletions(-)

apps/web/src/components/BranchToolbarBranchSelector.tsx+1-1
...@@ -918,7 +918,7 @@ export function BranchToolbarBranchSelector({...@@ -918,7 +918,7 @@ export function BranchToolbarBranchSelector({
918 </label>918 </label>
919 }919 }
920 />920 />
921 <TooltipPopup side="top" className="max-w-72 whitespace-normal leading-tight">921 <TooltipPopup side="top">
922 Creates the worktree from the latest matching branch on origin instead of your local922 Creates the worktree from the latest matching branch on origin instead of your local
923 branch.923 branch.
924 </TooltipPopup>924 </TooltipPopup>
apps/web/src/components/ChatMarkdown.tsx+2-10
...@@ -2209,10 +2209,7 @@ const MarkdownFileLink = memo(function MarkdownFileLink({...@@ -2209,10 +2209,7 @@ const MarkdownFileLink = memo(function MarkdownFileLink({
2209 )2209 )
2210 }2210 }
2211 />2211 />
2212 <TooltipPopup2212 <TooltipPopup side="top" variant="code">
2213 side="top"
2214 className="max-w-[min(40rem,calc(100vw-2rem))] font-mono text-[11px] leading-tight"
2215 >
2216 {/* The full path: the chip already shows the shortened form, and a link2213 {/* The full path: the chip already shows the shortened form, and a link
2217 to the workspace root collapses to a bare label that repeats it. */}2214 to the workspace root collapses to a bare label that repeats it. */}
2218 <div className="overflow-x-auto whitespace-nowrap [scrollbar-color:color-mix(in_srgb,var(--contrast-border)_78%,transparent)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-[color-mix(in_srgb,var(--contrast-border)_78%,transparent)] [&::-webkit-scrollbar-track]:bg-transparent">2215 <div className="overflow-x-auto whitespace-nowrap [scrollbar-color:color-mix(in_srgb,var(--contrast-border)_78%,transparent)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar]:h-1.5 [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-[color-mix(in_srgb,var(--contrast-border)_78%,transparent)] [&::-webkit-scrollbar-track]:bg-transparent">
...@@ -3062,12 +3059,7 @@ const CHAT_MARKDOWN_COMPONENTS = {...@@ -3062,12 +3059,7 @@ const CHAT_MARKDOWN_COMPONENTS = {
3062 return (3059 return (
3063 <Tooltip>3060 <Tooltip>
3064 <TooltipTrigger render={link} />3061 <TooltipTrigger render={link} />
3065 <TooltipPopup3062 <TooltipPopup side="top">{href}</TooltipPopup>
3066 side="top"
3067 className="max-w-[min(36rem,calc(100vw-2rem))] whitespace-normal leading-tight wrap-anywhere"
3068 >
3069 {href}
3070 </TooltipPopup>
3071 </Tooltip>3063 </Tooltip>
3072 );3064 );
3073 }3065 }
apps/web/src/components/ChatView.tsx+1-1
...@@ -6533,7 +6533,7 @@ export default function ChatView(props: ChatViewProps) {...@@ -6533,7 +6533,7 @@ export default function ChatView(props: ChatViewProps) {
6533 </code>6533 </code>
6534 }6534 }
6535 />6535 />
6536 <TooltipPopup side="top" className="max-w-80">6536 <TooltipPopup side="top">
6537 This thread last ran on {localCheckoutBranchMismatch.threadBranch}. Sending will6537 This thread last ran on {localCheckoutBranchMismatch.threadBranch}. Sending will
6538 continue on {localCheckoutBranchMismatch.currentBranch}.6538 continue on {localCheckoutBranchMismatch.currentBranch}.
6539 </TooltipPopup>6539 </TooltipPopup>
apps/web/src/components/ComposerPromptEditorTiptap.tsx+1-6
...@@ -237,12 +237,7 @@ function ComposerMentionNodeView({ node }: NodeViewProps) {...@@ -237,12 +237,7 @@ function ComposerMentionNodeView({ node }: NodeViewProps) {
237 <NodeViewWrapper as="span" className={COMPOSER_INLINE_CHIP_DECORATOR_CLASS_NAME}>237 <NodeViewWrapper as="span" className={COMPOSER_INLINE_CHIP_DECORATOR_CLASS_NAME}>
238 <Tooltip>238 <Tooltip>
239 <TooltipTrigger render={chip} />239 <TooltipTrigger render={chip} />
240 <TooltipPopup240 <TooltipPopup side="top">{path}</TooltipPopup>
241 side="top"
242 className="max-w-120 whitespace-normal leading-tight wrap-anywhere"
243 >
244 {path}
245 </TooltipPopup>
246 </Tooltip>241 </Tooltip>
247 </NodeViewWrapper>242 </NodeViewWrapper>
248 );243 );
apps/web/src/components/ConfirmDialogHost.tsx+1-1
...@@ -77,7 +77,7 @@ export function ConfirmDialogHost() {...@@ -77,7 +77,7 @@ export function ConfirmDialogHost() {
77 >77 >
78 <AlertDialogPopup>78 <AlertDialogPopup>
79 <AlertDialogHeader>79 <AlertDialogHeader>
80 <AlertDialogTitle className="wrap-anywhere">{copy.title}</AlertDialogTitle>80 <AlertDialogTitle>{copy.title}</AlertDialogTitle>
81 {copy.description ? (81 {copy.description ? (
82 <AlertDialogDescription className="whitespace-pre-line">82 <AlertDialogDescription className="whitespace-pre-line">
83 {copy.description}83 {copy.description}
apps/web/src/components/ConnectionStatusDot.tsx+1-1
...@@ -69,7 +69,7 @@ export function ConnectionStatusDot({...@@ -69,7 +69,7 @@ export function ConnectionStatusDot({
69 return (69 return (
70 <Tooltip>70 <Tooltip>
71 <TooltipTrigger render={dot} />71 <TooltipTrigger render={dot} />
72 <TooltipPopup side="top" className="max-w-80 whitespace-pre-wrap leading-tight">72 <TooltipPopup side="top" className="whitespace-pre-wrap">
73 {tooltipText}73 {tooltipText}
74 </TooltipPopup>74 </TooltipPopup>
75 </Tooltip>75 </Tooltip>
apps/web/src/components/GitActionsControl.tsx+1-1
...@@ -659,7 +659,7 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {...@@ -659,7 +659,7 @@ function PublishRepositoryDialog(props: PublishRepositoryDialogProps) {
659 </Button>659 </Button>
660 }660 }
661 />661 />
662 <TooltipPopup side="top" align="end" className="max-w-72">662 <TooltipPopup side="top" align="end">
663 {readiness.hint ??663 {readiness.hint ??
664 "Open Settings -> Source Control to configure this provider."}664 "Open Settings -> Source Control to configure this provider."}
665 </TooltipPopup>665 </TooltipPopup>
apps/web/src/components/LegacySidebar.tsx+1-3
...@@ -785,9 +785,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP...@@ -785,9 +785,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
785 </span>785 </span>
786 }786 }
787 />787 />
788 <TooltipPopup side="top" className="max-w-80 whitespace-normal leading-tight">788 <TooltipPopup side="top">{thread.title}</TooltipPopup>
789 {thread.title}
790 </TooltipPopup>
791 </Tooltip>789 </Tooltip>
792 )}790 )}
793 </div>791 </div>
apps/web/src/components/ServerUpdateAction.tsx+1-3
...@@ -163,9 +163,7 @@ export function ServerUpdateProgress({...@@ -163,9 +163,7 @@ export function ServerUpdateProgress({
163 <span className="size-1.5 shrink-0 rounded-full bg-destructive" aria-hidden="true" />163 <span className="size-1.5 shrink-0 rounded-full bg-destructive" aria-hidden="true" />
164 <Tooltip>164 <Tooltip>
165 <TooltipTrigger render={<span className="min-w-0 truncate">{state.message}</span>} />165 <TooltipTrigger render={<span className="min-w-0 truncate">{state.message}</span>} />
166 <TooltipPopup side="top" className="max-w-80">166 <TooltipPopup side="top">{state.message}</TooltipPopup>
167 {state.message}
168 </TooltipPopup>
169 </Tooltip>167 </Tooltip>
170 </div>168 </div>
171 );169 );
apps/web/src/components/Sidebar.tsx+1-1
...@@ -350,7 +350,7 @@ function SidebarThreadTooltip({...@@ -350,7 +350,7 @@ function SidebarThreadTooltip({
350 align="start"350 align="start"
351 sideOffset={4}351 sideOffset={4}
352 variant="glass"352 variant="glass"
353 className="max-w-80 text-left whitespace-normal [&_[data-slot=tooltip-viewport]]:p-0"353 className="[&_[data-slot=tooltip-viewport]]:p-0"
354 >354 >
355 <div className="flex min-w-0 max-w-80 flex-col gap-2 p-[var(--floating-content-inset)]">355 <div className="flex min-w-0 max-w-80 flex-col gap-2 p-[var(--floating-content-inset)]">
356 <div className="min-w-0 truncate text-xs leading-tight font-medium text-foreground">356 <div className="min-w-0 truncate text-xs leading-tight font-medium text-foreground">
apps/web/src/components/StartTruncatedPath.tsx+1-1
...@@ -17,7 +17,7 @@ export function StartTruncatedPath({ path, className }: { path: string; classNam...@@ -17,7 +17,7 @@ export function StartTruncatedPath({ path, className }: { path: string; classNam
17 >17 >
18 <bdi>{path}</bdi>18 <bdi>{path}</bdi>
19 </TooltipTrigger>19 </TooltipTrigger>
20 <TooltipPopup side="top" className="max-w-sm break-all font-mono">20 <TooltipPopup side="top" variant="code">
21 {path}21 {path}
22 </TooltipPopup>22 </TooltipPopup>
23 </Tooltip>23 </Tooltip>
apps/web/src/components/chat/ChatComposer.tsx+4-22
...@@ -6569,10 +6569,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -6569,10 +6569,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
6569 </span>6569 </span>
6570 }6570 }
6571 />6571 />
6572 <TooltipPopup6572 <TooltipPopup side="top">
6573 side="top"
6574 className="max-w-64 whitespace-normal leading-tight"
6575 >
6576 Draft attachment could not be saved locally and may be lost on6573 Draft attachment could not be saved locally and may be lost on
6577 navigation.6574 navigation.
6578 </TooltipPopup>6575 </TooltipPopup>
...@@ -6604,12 +6601,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -6604,12 +6601,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
6604 >6601 >
6605 <RefreshIcon />6602 <RefreshIcon />
6606 </TooltipTrigger>6603 </TooltipTrigger>
6607 <TooltipPopup6604 <TooltipPopup side="top">{upload.reason}</TooltipPopup>
6608 side="top"
6609 className="max-w-64 whitespace-normal leading-tight"
6610 >
6611 {upload.reason}
6612 </TooltipPopup>
6613 </Tooltip>6605 </Tooltip>
6614 )}6606 )}
6615 <Button6607 <Button
...@@ -6704,12 +6696,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -6704,12 +6696,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
6704 >6696 >
6705 <RefreshIcon />6697 <RefreshIcon />
6706 </TooltipTrigger>6698 </TooltipTrigger>
6707 <TooltipPopup6699 <TooltipPopup side="top">{upload.reason}</TooltipPopup>
6708 side="top"
6709 className="max-w-64 whitespace-normal leading-tight"
6710 >
6711 {upload.reason}
6712 </TooltipPopup>
6713 </Tooltip>6700 </Tooltip>
6714 )}6701 )}
6715 <Button6702 <Button
...@@ -6787,12 +6774,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)...@@ -6787,12 +6774,7 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
6787 >6774 >
6788 <RefreshIcon />6775 <RefreshIcon />
6789 </TooltipTrigger>6776 </TooltipTrigger>
6790 <TooltipPopup6777 <TooltipPopup side="top">{upload.reason}</TooltipPopup>
6791 side="top"
6792 className="max-w-64 whitespace-normal leading-tight"
6793 >
6794 {upload.reason}
6795 </TooltipPopup>
6796 </Tooltip>6778 </Tooltip>
6797 ) : null}6779 ) : null}
6798 <Button6780 <Button
apps/web/src/components/chat/ComposerPendingApprovalActions.tsx+2-6
...@@ -59,9 +59,7 @@ export const ComposerPendingApprovalActions = memo(function ComposerPendingAppro...@@ -59,9 +59,7 @@ export const ComposerPendingApprovalActions = memo(function ComposerPendingAppro
59 return option.warning ? (59 return option.warning ? (
60 <Tooltip key={option.decision}>60 <Tooltip key={option.decision}>
61 <TooltipTrigger render={button} />61 <TooltipTrigger render={button} />
62 <TooltipPopup side="top" className="max-w-72 text-xs leading-snug">62 <TooltipPopup side="top">{option.warning}</TooltipPopup>
63 {option.warning}
64 </TooltipPopup>
65 </Tooltip>63 </Tooltip>
66 ) : (64 ) : (
67 button65 button
...@@ -98,9 +96,7 @@ export const ComposerPendingApprovalActions = memo(function ComposerPendingAppro...@@ -98,9 +96,7 @@ export const ComposerPendingApprovalActions = memo(function ComposerPendingAppro
98 return option.warning ? (96 return option.warning ? (
99 <Tooltip key={option.decision}>97 <Tooltip key={option.decision}>
100 <TooltipTrigger render={item} />98 <TooltipTrigger render={item} />
101 <TooltipPopup side="top" className="max-w-64 text-xs leading-snug">99 <TooltipPopup side="top">{option.warning}</TooltipPopup>
102 {option.warning}
103 </TooltipPopup>
104 </Tooltip>100 </Tooltip>
105 ) : (101 ) : (
106 item102 item
apps/web/src/components/chat/ComposerServerUpdateStatus.tsx+1-1
...@@ -56,7 +56,7 @@ export function ComposerServerUpdateStatus({...@@ -56,7 +56,7 @@ export function ComposerServerUpdateStatus({
56 </button>56 </button>
57 }57 }
58 />58 />
59 <TooltipPopup side="top" className="max-w-80">59 <TooltipPopup side="top">
60 {title}: {detail}60 {title}: {detail}
61 </TooltipPopup>61 </TooltipPopup>
62 </Tooltip>62 </Tooltip>
apps/web/src/components/chat/DraftHeroHeadline.tsx+2-6
...@@ -146,9 +146,7 @@ export function DraftHeroHeadline({...@@ -146,9 +146,7 @@ export function DraftHeroHeadline({
146 {activeProjectDisplayName ?? "Choose a project"}146 {activeProjectDisplayName ?? "Choose a project"}
147 </TooltipTrigger>147 </TooltipTrigger>
148 {activeProjectDisplayName ? (148 {activeProjectDisplayName ? (
149 <TooltipPopup side="top" className="max-w-80">149 <TooltipPopup side="top">{activeProjectDisplayName}</TooltipPopup>
150 {activeProjectDisplayName}
151 </TooltipPopup>
152 ) : null}150 ) : null}
153 </Tooltip>151 </Tooltip>
154 <MenuPopup align="center" className="max-h-80 min-w-40! w-max max-w-64 overflow-y-auto">152 <MenuPopup align="center" className="max-h-80 min-w-40! w-max max-w-64 overflow-y-auto">
...@@ -202,9 +200,7 @@ export function DraftHeroHeadline({...@@ -202,9 +200,7 @@ export function DraftHeroHeadline({
202 <TooltipTrigger render={<span className="block min-w-0 truncate" />}>200 <TooltipTrigger render={<span className="block min-w-0 truncate" />}>
203 {group.displayName}201 {group.displayName}
204 </TooltipTrigger>202 </TooltipTrigger>
205 <TooltipPopup side="top" className="max-w-80">203 <TooltipPopup side="top">{group.displayName}</TooltipPopup>
206 {group.displayName}
207 </TooltipPopup>
208 </Tooltip>204 </Tooltip>
209 {showProjectEnvironments ? (205 {showProjectEnvironments ? (
210 <ProjectEnvironmentBadge206 <ProjectEnvironmentBadge
apps/web/src/components/chat/ModelListRow.tsx+1-1
...@@ -145,7 +145,7 @@ export const ModelListRow = memo(function ModelListRow(props: {...@@ -145,7 +145,7 @@ export const ModelListRow = memo(function ModelListRow(props: {
145 return (145 return (
146 <Tooltip>146 <Tooltip>
147 <TooltipTrigger render={row} />147 <TooltipTrigger render={row} />
148 <TooltipPopup side="left" align="center" className="max-w-64 text-balance leading-snug">148 <TooltipPopup side="left" align="center">
149 {props.disabledReason}149 {props.disabledReason}
150 </TooltipPopup>150 </TooltipPopup>
151 </Tooltip>151 </Tooltip>
apps/web/src/components/chat/ModelPickerSidebar.tsx-3
...@@ -39,7 +39,6 @@ const NEW_BADGE_CLASS = `${BADGE_BASE_CLASS} text-update-foreground `;...@@ -39,7 +39,6 @@ const NEW_BADGE_CLASS = `${BADGE_BASE_CLASS} text-update-foreground `;
39/** Opens toward the rail so the list stays readable (not over the model names). */39/** Opens toward the rail so the list stays readable (not over the model names). */
40const PICKER_TOOLTIP_SIDE = "left" as const;40const PICKER_TOOLTIP_SIDE = "left" as const;
41const PICKER_TOOLTIP_SIDE_OFFSET = 8;41const PICKER_TOOLTIP_SIDE_OFFSET = 8;
42const PICKER_TOOLTIP_CLASS = "max-w-64 text-balance font-normal leading-snug";
4342
44export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {43export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
45 selectedInstanceId: ProviderInstanceId | "favorites";44 selectedInstanceId: ProviderInstanceId | "favorites";
...@@ -139,7 +138,6 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {...@@ -139,7 +138,6 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
139 side={PICKER_TOOLTIP_SIDE}138 side={PICKER_TOOLTIP_SIDE}
140 sideOffset={PICKER_TOOLTIP_SIDE_OFFSET}139 sideOffset={PICKER_TOOLTIP_SIDE_OFFSET}
141 align="center"140 align="center"
142 className={PICKER_TOOLTIP_CLASS}
143 >141 >
144 Favorites142 Favorites
145 </TooltipPopup>143 </TooltipPopup>
...@@ -242,7 +240,6 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {...@@ -242,7 +240,6 @@ export const ModelPickerSidebar = memo(function ModelPickerSidebar(props: {
242 side={PICKER_TOOLTIP_SIDE}240 side={PICKER_TOOLTIP_SIDE}
243 sideOffset={PICKER_TOOLTIP_SIDE_OFFSET}241 sideOffset={PICKER_TOOLTIP_SIDE_OFFSET}
244 align="center"242 align="center"
245 className={PICKER_TOOLTIP_CLASS}
246 >243 >
247 {tooltip}244 {tooltip}
248 </TooltipPopup>245 </TooltipPopup>
apps/web/src/components/chat/ProviderStatusBanner.tsx+1-1
...@@ -100,7 +100,7 @@ export const ProviderStatusBanner = memo(function ProviderStatusBanner({...@@ -100,7 +100,7 @@ export const ProviderStatusBanner = memo(function ProviderStatusBanner({
100 <TooltipTrigger100 <TooltipTrigger
101 render={<div className="line-clamp-3 text-muted-foreground">{message}</div>}101 render={<div className="line-clamp-3 text-muted-foreground">{message}</div>}
102 />102 />
103 <TooltipPopup side="top" className="max-w-96 whitespace-pre-wrap">103 <TooltipPopup side="top" className="whitespace-pre-wrap">
104 {message}104 {message}
105 </TooltipPopup>105 </TooltipPopup>
106 </Tooltip>106 </Tooltip>
apps/web/src/components/chat/ThreadErrorBanner.tsx+1-1
...@@ -53,7 +53,7 @@ export const ThreadErrorBanner = memo(function ThreadErrorBanner({...@@ -53,7 +53,7 @@ export const ThreadErrorBanner = memo(function ThreadErrorBanner({
53 <AlertDescription>53 <AlertDescription>
54 <Tooltip>54 <Tooltip>
55 <TooltipTrigger render={<div className="line-clamp-3" />}>{error}</TooltipTrigger>55 <TooltipTrigger render={<div className="line-clamp-3" />}>{error}</TooltipTrigger>
56 <TooltipPopup side="top" className="max-w-96 whitespace-pre-wrap">56 <TooltipPopup side="top" className="whitespace-pre-wrap">
57 {error}57 {error}
58 </TooltipPopup>58 </TooltipPopup>
59 </Tooltip>59 </Tooltip>
apps/web/src/components/cloud/CloudEnvironmentConnectList.tsx+2-6
...@@ -393,9 +393,7 @@ export function CloudEnvironmentConnectRows({...@@ -393,9 +393,7 @@ export function CloudEnvironmentConnectRows({
393 ? "Unavailable"393 ? "Unavailable"
394 : "Checking…"))}394 : "Checking…"))}
395 </TooltipTrigger>395 </TooltipTrigger>
396 <TooltipPopup className="max-w-80 break-words">396 <TooltipPopup>{unsupportedDetail ?? statusText}</TooltipPopup>
397 {unsupportedDetail ?? statusText}
398 </TooltipPopup>
399 </Tooltip>397 </Tooltip>
400 </label>398 </label>
401 );399 );
...@@ -454,9 +452,7 @@ export function CloudEnvironmentConnectRows({...@@ -454,9 +452,7 @@ export function CloudEnvironmentConnectRows({
454 Add452 Add
455 </Button>453 </Button>
456 </TooltipTrigger>454 </TooltipTrigger>
457 <TooltipPopup className="max-w-80 break-words">455 <TooltipPopup>{unsupportedDetail ?? "Client not supported"}</TooltipPopup>
458 {unsupportedDetail ?? "Client not supported"}
459 </TooltipPopup>
460 </Tooltip>456 </Tooltip>
461 ) : savedConnection ? (457 ) : savedConnection ? (
462 <Button size="sm" variant="outline" disabled>458 <Button size="sm" variant="outline" disabled>
apps/web/src/components/composerContextPresentation.tsx+1-1
...@@ -199,7 +199,7 @@ function ImageContextChip(props: {...@@ -199,7 +199,7 @@ function ImageContextChip(props: {
199 />199 />
200 }200 }
201 />201 />
202 <TooltipPopup side="top" className="max-w-80 whitespace-pre-wrap leading-tight">202 <TooltipPopup side="top" className="whitespace-pre-wrap">
203 {attachmentTooltip(props.record, props.upload)}203 {attachmentTooltip(props.record, props.upload)}
204 </TooltipPopup>204 </TooltipPopup>
205 </Tooltip>205 </Tooltip>
apps/web/src/components/contextChipParts.tsx+1-1
...@@ -301,7 +301,7 @@ export function FileChip(props: {...@@ -301,7 +301,7 @@ export function FileChip(props: {
301 )301 )
302 }302 }
303 />303 />
304 <TooltipPopup side="top" className="max-w-80 whitespace-pre-wrap leading-tight">304 <TooltipPopup side="top" className="whitespace-pre-wrap">
305 {props.tooltip}305 {props.tooltip}
306 </TooltipPopup>306 </TooltipPopup>
307 </Tooltip>307 </Tooltip>
apps/web/src/components/files/FileBreadcrumbs.tsx+2-6
...@@ -61,9 +61,7 @@ function BreadcrumbLabel(props: {...@@ -61,9 +61,7 @@ function BreadcrumbLabel(props: {
61 >61 >
62 {props.label}62 {props.label}
63 </TooltipTrigger>63 </TooltipTrigger>
64 <TooltipPopup side="top" className="max-w-80">64 <TooltipPopup side="top">{props.pathLabel}</TooltipPopup>
65 {props.pathLabel}
66 </TooltipPopup>
67 </Tooltip>65 </Tooltip>
68 );66 );
69}67}
...@@ -241,9 +239,7 @@ function DirectoryBreadcrumb(props: FileBreadcrumbsProps & { readonly crumb: Fil...@@ -241,9 +239,7 @@ function DirectoryBreadcrumb(props: FileBreadcrumbsProps & { readonly crumb: Fil
241 >239 >
242 <span className="block truncate">{props.crumb.label}</span>240 <span className="block truncate">{props.crumb.label}</span>
243 </TooltipTrigger>241 </TooltipTrigger>
244 <TooltipPopup side="top" className="max-w-80">242 <TooltipPopup side="top">{props.crumb.path || props.projectName}</TooltipPopup>
245 {props.crumb.path || props.projectName}
246 </TooltipPopup>
247 </Tooltip>243 </Tooltip>
248 {open ? (244 {open ? (
249 <BreadcrumbMenuContent245 <BreadcrumbMenuContent
apps/web/src/components/media/MediaActions.tsx+1-3
...@@ -186,9 +186,7 @@ export function MediaActions({...@@ -186,9 +186,7 @@ export function MediaActions({
186 void showMenu({ x: bounds.left, y: bounds.bottom });186 void showMenu({ x: bounds.left, y: bounds.bottom });
187 }}187 }}
188 />188 />
189 <TooltipPopup className="max-w-[min(40rem,calc(100vw-2rem))] break-all font-mono text-[11px] leading-tight">189 <TooltipPopup variant="code">{tooltip}</TooltipPopup>
190 {tooltip}
191 </TooltipPopup>
192 </Tooltip>190 </Tooltip>
193 );191 );
194}192}
apps/web/src/components/onboarding/WelcomeWizard.tsx+1-1
...@@ -1453,7 +1453,7 @@ function ImportCandidateRow({...@@ -1453,7 +1453,7 @@ function ImportCandidateRow({
1453 </span>1453 </span>
1454 ) : null}1454 ) : null}
1455 </TooltipTrigger>1455 </TooltipTrigger>
1456 <TooltipPopup className="max-w-96 break-all font-mono">{candidate.path}</TooltipPopup>1456 <TooltipPopup variant="code">{candidate.path}</TooltipPopup>
1457 </Tooltip>1457 </Tooltip>
1458 <ImportRowMeta1458 <ImportRowMeta
1459 sources={nested ? null : candidate.sources}1459 sources={nested ? null : candidate.sources}
apps/web/src/components/pullRequest/PullRequestCopyableCode.tsx+1-1
...@@ -58,7 +58,7 @@ export function PullRequestCopyableCode({...@@ -58,7 +58,7 @@ export function PullRequestCopyableCode({
58 Copied58 Copied
59 </span>59 </span>
60 </TooltipTrigger>60 </TooltipTrigger>
61 <TooltipPopup className="max-w-96 wrap-anywhere font-mono" side={tooltipSide}>61 <TooltipPopup variant="code" side={tooltipSide}>
62 {`${isCopied ? "Copied" : copyLabel}: ${value}`}62 {`${isCopied ? "Copied" : copyLabel}: ${value}`}
63 </TooltipPopup>63 </TooltipPopup>
64 </Tooltip>64 </Tooltip>
apps/web/src/components/pullRequest/PullRequestListFilters.tsx+1-3
...@@ -202,9 +202,7 @@ function PullRequestFilterRadioGroup<Value extends string>({...@@ -202,9 +202,7 @@ function PullRequestFilterRadioGroup<Value extends string>({
202 return (202 return (
203 <Tooltip key={option.value}>203 <Tooltip key={option.value}>
204 <TooltipTrigger render={item} />204 <TooltipTrigger render={item} />
205 <TooltipPopup side="top" className="max-w-80">205 <TooltipPopup side="top">{option.unavailable}</TooltipPopup>
206 {option.unavailable}
207 </TooltipPopup>
208 </Tooltip>206 </Tooltip>
209 );207 );
210 })}208 })}
apps/web/src/components/settings/ConnectionsSettings.tsx+4-10
...@@ -925,9 +925,7 @@ const PairingLinkListRow = memo(function PairingLinkListRow({...@@ -925,9 +925,7 @@ const PairingLinkListRow = memo(function PairingLinkListRow({
925 </code>925 </code>
926 }926 }
927 />927 />
928 <TooltipPopup side="top" className="max-w-80 break-all">928 <TooltipPopup side="top">{qrPairingUrl}</TooltipPopup>
929 {qrPairingUrl}
930 </TooltipPopup>
931 </Tooltip>929 </Tooltip>
932 <Button930 <Button
933 size="xs"931 size="xs"
...@@ -1334,9 +1332,7 @@ const AdvertisedEndpointListRow = memo(function AdvertisedEndpointListRow({...@@ -1334,9 +1332,7 @@ const AdvertisedEndpointListRow = memo(function AdvertisedEndpointListRow({
1334 </p>1332 </p>
1335 }1333 }
1336 />1334 />
1337 <TooltipPopup side="top" className="max-w-80">1335 <TooltipPopup side="top">{endpoint.httpBaseUrl}</TooltipPopup>
1338 {endpoint.httpBaseUrl}
1339 </TooltipPopup>
1340 </Tooltip>1336 </Tooltip>
1341 ) : null}1337 ) : null}
1342 {!isAvailable ? (1338 {!isAvailable ? (
...@@ -1573,7 +1569,7 @@ function SavedBackendListRow({...@@ -1573,7 +1569,7 @@ function SavedBackendListRow({
1573 >1569 >
1574 {subtitleText}1570 {subtitleText}
1575 </TooltipTrigger>1571 </TooltipTrigger>
1576 <TooltipPopup side="top" className="max-w-80 whitespace-pre-wrap leading-tight">1572 <TooltipPopup side="top" className="whitespace-pre-wrap">
1577 {unsupported1573 {unsupported
1578 ? (environment.connection.error ?? connectionStatusText(environment.connection))1574 ? (environment.connection.error ?? connectionStatusText(environment.connection))
1579 : enabled1575 : enabled
...@@ -3635,9 +3631,7 @@ export function ConnectionsSettings() {...@@ -3635,9 +3631,7 @@ export function ConnectionsSettings() {
3635 }3631 }
3636 />3632 />
3637 {pendingTailscaleServeBaseUrl ? (3633 {pendingTailscaleServeBaseUrl ? (
3638 <TooltipPopup side="top" className="max-w-80">3634 <TooltipPopup side="top">{pendingTailscaleServeBaseUrl}</TooltipPopup>
3639 {pendingTailscaleServeBaseUrl}
3640 </TooltipPopup>
3641 ) : null}3635 ) : null}
3642 </Tooltip>3636 </Tooltip>
3643 </div>3637 </div>
apps/web/src/components/settings/DiagnosticsSettings.tsx+6-26
...@@ -109,12 +109,7 @@ function StatBlock({...@@ -109,12 +109,7 @@ function StatBlock({
109 </button>109 </button>
110 }110 }
111 />111 />
112 <TooltipPopup112 <TooltipPopup side="top">{tooltip}</TooltipPopup>
113 side="top"
114 className="max-w-[min(300px,calc(100vw-2rem))] whitespace-normal text-left text-[11px] leading-relaxed text-wrap"
115 >
116 {tooltip}
117 </TooltipPopup>
118 </Tooltip>113 </Tooltip>
119 ) : null}114 ) : null}
120 </div>115 </div>
...@@ -225,10 +220,7 @@ function TraceIdCell({ traceId }: { traceId: string }) {...@@ -225,10 +220,7 @@ function TraceIdCell({ traceId }: { traceId: string }) {
225 </span>220 </span>
226 }221 }
227 />222 />
228 <TooltipPopup223 <TooltipPopup side="top" variant="code">
229 side="top"
230 className="max-w-[min(520px,calc(100vw-2rem))] break-all font-mono text-[11px]"
231 >
232 {traceId}224 {traceId}
233 </TooltipPopup>225 </TooltipPopup>
234 </Tooltip>226 </Tooltip>
...@@ -300,10 +292,7 @@ function ProcessNameCell({...@@ -300,10 +292,7 @@ function ProcessNameCell({
300 <TooltipTrigger292 <TooltipTrigger
301 render={<span className="min-w-0 truncate font-medium text-foreground">{name}</span>}293 render={<span className="min-w-0 truncate font-medium text-foreground">{name}</span>}
302 />294 />
303 <TooltipPopup295 <TooltipPopup side="top" variant="code">
304 side="top"
305 className="max-w-[min(440px,calc(100vw-2rem))] whitespace-normal break-words text-left font-mono text-[11px] leading-relaxed text-wrap"
306 >
307 {process.command}296 {process.command}
308 </TooltipPopup>297 </TooltipPopup>
309 </Tooltip>298 </Tooltip>
...@@ -411,10 +400,7 @@ function ProcessDiagnosticsTable({...@@ -411,10 +400,7 @@ function ProcessDiagnosticsTable({
411 <TooltipTrigger400 <TooltipTrigger
412 render={<span className="block truncate">{process.command}</span>}401 render={<span className="block truncate">{process.command}</span>}
413 />402 />
414 <TooltipPopup403 <TooltipPopup side="top" variant="code">
415 side="top"
416 className="max-w-[min(440px,calc(100vw-2rem))] whitespace-normal break-words text-left font-mono text-[11px] leading-relaxed text-wrap"
417 >
418 {process.command}404 {process.command}
419 </TooltipPopup>405 </TooltipPopup>
420 </Tooltip>406 </Tooltip>
...@@ -484,10 +470,7 @@ function ResourceHistoryProcessNameCell({...@@ -484,10 +470,7 @@ function ResourceHistoryProcessNameCell({
484 <TooltipTrigger470 <TooltipTrigger
485 render={<span className="min-w-0 truncate font-medium text-foreground">{name}</span>}471 render={<span className="min-w-0 truncate font-medium text-foreground">{name}</span>}
486 />472 />
487 <TooltipPopup473 <TooltipPopup side="top" variant="code">
488 side="top"
489 className="max-w-[min(440px,calc(100vw-2rem))] whitespace-normal break-words text-left font-mono text-[11px] leading-relaxed text-wrap"
490 >
491 {process.command}474 {process.command}
492 </TooltipPopup>475 </TooltipPopup>
493 </Tooltip>476 </Tooltip>
...@@ -654,10 +637,7 @@ function ProcessResourceHistoryTable({...@@ -654,10 +637,7 @@ function ProcessResourceHistoryTable({
654 <TooltipTrigger637 <TooltipTrigger
655 render={<span className="block truncate">{process.command}</span>}638 render={<span className="block truncate">{process.command}</span>}
656 />639 />
657 <TooltipPopup640 <TooltipPopup side="top" variant="code">
658 side="top"
659 className="max-w-[min(440px,calc(100vw-2rem))] whitespace-normal break-words text-left font-mono text-[11px] leading-relaxed text-wrap"
660 >
661 {process.command}641 {process.command}
662 </TooltipPopup>642 </TooltipPopup>
663 </Tooltip>643 </Tooltip>
apps/web/src/components/settings/KeybindingsSettings.tsx+1-3
...@@ -261,9 +261,7 @@ function WarningTooltipIcon({...@@ -261,9 +261,7 @@ function WarningTooltipIcon({
261 >261 >
262 <TriangleAlertIcon className="size-3.5" />262 <TriangleAlertIcon className="size-3.5" />
263 </TooltipTrigger>263 </TooltipTrigger>
264 <TooltipPopup side="top" className="max-w-72 whitespace-normal leading-relaxed">264 <TooltipPopup side="top">{children}</TooltipPopup>
265 {children}
266 </TooltipPopup>
267 </Tooltip>265 </Tooltip>
268 );266 );
269}267}
apps/web/src/components/settings/ResourceTelemetryDiagnostics.tsx+2-8
...@@ -503,10 +503,7 @@ function ProcessTreeName({...@@ -503,10 +503,7 @@ function ProcessTreeName({
503 <TooltipTrigger503 <TooltipTrigger
504 render={<span className="min-w-0 truncate font-medium text-foreground">{name}</span>}504 render={<span className="min-w-0 truncate font-medium text-foreground">{name}</span>}
505 />505 />
506 <TooltipPopup506 <TooltipPopup side="top" variant="code">
507 side="top"
508 className="max-w-[min(520px,calc(100vw-2rem))] whitespace-normal break-words text-left font-mono text-[11px]"
509 >
510 {process.command || process.name}507 {process.command || process.name}
511 </TooltipPopup>508 </TooltipPopup>
512 </Tooltip>509 </Tooltip>
...@@ -721,10 +718,7 @@ function HistoryProcessTable({...@@ -721,10 +718,7 @@ function HistoryProcessTable({
721 </span>718 </span>
722 }719 }
723 />720 />
724 <TooltipPopup721 <TooltipPopup side="top" variant="code">
725 side="top"
726 className="max-w-[min(520px,calc(100vw-2rem))] whitespace-normal break-words text-left font-mono text-[11px]"
727 >
728 {process.command || process.name}722 {process.command || process.name}
729 </TooltipPopup>723 </TooltipPopup>
730 </Tooltip>724 </Tooltip>
apps/web/src/components/settings/settingsLayout.tsx+2-6
...@@ -156,9 +156,7 @@ export function PolicyTooltip({ children }: { readonly children: string }) {...@@ -156,9 +156,7 @@ export function PolicyTooltip({ children }: { readonly children: string }) {
156 </Button>156 </Button>
157 }157 }
158 />158 />
159 <TooltipPopup side="top" className="max-w-72">159 <TooltipPopup side="top">{children}</TooltipPopup>
160 {children}
161 </TooltipPopup>
162 </Tooltip>160 </Tooltip>
163 );161 );
164}162}
...@@ -372,9 +370,7 @@ export function SettingsRow({...@@ -372,9 +370,7 @@ export function SettingsRow({
372 {control}370 {control}
373 </div>371 </div>
374 </TooltipTrigger>372 </TooltipTrigger>
375 <TooltipPopup side="top" className="max-w-72">373 <TooltipPopup side="top">{message}</TooltipPopup>
376 {message}
377 </TooltipPopup>
378 </Tooltip>374 </Tooltip>
379 );375 );
380 // A mixed selection keeps the real control with "Mixed" as its placeholder376 // A mixed selection keeps the real control with "Mixed" as its placeholder
apps/web/src/components/ui/alert-dialog.tsx+1-1
...@@ -101,7 +101,7 @@ function AlertDialogFooter({...@@ -101,7 +101,7 @@ function AlertDialogFooter({
101function AlertDialogTitle({ className, ...props }: AlertDialogPrimitive.Title.Props) {101function AlertDialogTitle({ className, ...props }: AlertDialogPrimitive.Title.Props) {
102 return (102 return (
103 <AlertDialogPrimitive.Title103 <AlertDialogPrimitive.Title
104 className={cn("font-heading font-semibold text-xl leading-none", className)}104 className={cn("wrap-anywhere font-heading font-semibold text-xl leading-none", className)}
105 data-slot="alert-dialog-title"105 data-slot="alert-dialog-title"
106 {...props}106 {...props}
107 />107 />
apps/web/src/components/ui/dialog.tsx+1-1
...@@ -145,7 +145,7 @@ function DialogFooter({...@@ -145,7 +145,7 @@ function DialogFooter({
145function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {145function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
146 return (146 return (
147 <DialogPrimitive.Title147 <DialogPrimitive.Title
148 className={cn("font-heading font-semibold text-xl leading-none", className)}148 className={cn("wrap-anywhere font-heading font-semibold text-xl leading-none", className)}
149 data-slot="dialog-title"149 data-slot="dialog-title"
150 {...props}150 {...props}
151 />151 />
apps/web/src/components/ui/tooltip.tsx+6-1
...@@ -23,7 +23,8 @@ function TooltipPopup({...@@ -23,7 +23,8 @@ function TooltipPopup({
23 align?: TooltipPrimitive.Positioner.Props["align"];23 align?: TooltipPrimitive.Positioner.Props["align"];
24 side?: TooltipPrimitive.Positioner.Props["side"];24 side?: TooltipPrimitive.Positioner.Props["side"];
25 sideOffset?: TooltipPrimitive.Positioner.Props["sideOffset"];25 sideOffset?: TooltipPrimitive.Positioner.Props["sideOffset"];
26 variant?: "default" | "glass";26 /** `code` renders monospace content that breaks anywhere, for paths and commands. */
27 variant?: "default" | "glass" | "code";
27 anchor?: TooltipPrimitive.Positioner.Props["anchor"];28 anchor?: TooltipPrimitive.Positioner.Props["anchor"];
28}) {29}) {
29 return (30 return (
...@@ -42,6 +43,10 @@ function TooltipPopup({...@@ -42,6 +43,10 @@ function TooltipPopup({
42 variant === "glass"43 variant === "glass"
43 ? "dropdown-glass shadow-xl shadow-black/25 before:hidden"44 ? "dropdown-glass shadow-xl shadow-black/25 before:hidden"
44 : "border bg-popover not-dark:bg-clip-padding shadow-md/5",45 : "border bg-popover not-dark:bg-clip-padding shadow-md/5",
46 // One wrap width for prose; code dumps get more room and break anywhere.
47 variant === "code"
48 ? "max-w-120 wrap-anywhere text-left font-mono text-[11px] leading-relaxed"
49 : "max-w-80 wrap-anywhere whitespace-normal leading-snug",
45 className,50 className,
46 )}51 )}
47 data-slot="tooltip-popup"52 data-slot="tooltip-popup"
apps/web/src/components/usage/UsageLimits.tsx+1-1
...@@ -127,7 +127,7 @@ function WindowBar({...@@ -127,7 +127,7 @@ function WindowBar({
127 />127 />
128 ) : null}128 ) : null}
129 </TooltipTrigger>129 </TooltipTrigger>
130 <TooltipPopup side="top" className="max-w-72 text-xs">130 <TooltipPopup side="top">
131 <div className="flex flex-col gap-0.5">131 <div className="flex flex-col gap-0.5">
132 <span className="text-foreground">132 <span className="text-foreground">
133 {remaining}% left{timeLeft !== null ? ` · ${timeLeft}% of the window left` : ""}133 {remaining}% left{timeLeft !== null ? ` · ${timeLeft}% of the window left` : ""}
apps/web/src/routes/_chat.pull-requests.tsx+1-3
...@@ -2280,9 +2280,7 @@ function CompactFilterMenu<Value extends string>({...@@ -2280,9 +2280,7 @@ function CompactFilterMenu<Value extends string>({
2280 ) : (2280 ) : (
2281 <Tooltip key={option.value}>2281 <Tooltip key={option.value}>
2282 <TooltipTrigger render={item} />2282 <TooltipTrigger render={item} />
2283 <TooltipPopup side="right" className="max-w-64 break-words">2283 <TooltipPopup side="right">{option.unavailable}</TooltipPopup>
2284 {option.unavailable}
2285 </TooltipPopup>
2286 </Tooltip>2284 </Tooltip>
2287 );2285 );
2288 })}2286 })}
scripts/lint-restyle-ceiling.ts+1-1
...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";...@@ -8,7 +8,7 @@ import * as NodeURL from "node:url";
8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the8// components/ui exports are migrated to variants (see vite.config.ts). This gate keeps the
9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you9// count from growing: CI fails when findings exceed the ceiling. Lower the ceiling when you
10// migrate a file, and delete this script when the rule becomes an error.10// migrate a file, and delete this script when the rule becomes an error.
11export const RESTYLE_CEILING = 981;11export const RESTYLE_CEILING = 921;
1212
13const RULE = "shadcn(no-restyle)";13const RULE = "shadcn(no-restyle)";
1414