Skip to content

Commit fcb9dbe

Browse files
committed
feat: add file path tooltips with centralized PathTooltip component
- Add PathTooltip component that wraps StandardTooltip with proper styling - Use maxWidth='min(300px,100vw)' via inline style to override defaults - Apply '[text-wrap:wrap]' class to override text-balance behavior - Add formatPathTooltip helper for consistent path content formatting - Update CodeAccordian, ChatRow, and BatchFilePermission to use PathTooltip - Centralizes tooltip behavior and reduces duplication Supersedes PR #8797.
1 parent f717863 commit fcb9dbe

File tree

5 files changed

+113
-15
lines changed

5 files changed

+113
-15
lines changed

webview-ui/src/components/chat/BatchFilePermission.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { memo } from "react"
22

33
import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
44
import { vscode } from "@src/utils/vscode"
5-
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
5+
import { formatPathTooltip } from "@src/utils/formatPathTooltip"
6+
import { PathTooltip } from "../ui/PathTooltip"
67

78
interface FilePermissionItem {
89
path: string
@@ -35,10 +36,18 @@ export const BatchFilePermission = memo(({ files = [], onPermissionResponse, ts
3536
<ToolUseBlockHeader
3637
onClick={() => vscode.postMessage({ type: "openFile", text: file.content })}>
3738
{file.path?.startsWith(".") && <span>.</span>}
38-
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
39-
{removeLeadingNonAlphanumeric(file.path ?? "") + "\u200E"}
40-
{file.lineSnippet && ` ${file.lineSnippet}`}
41-
</span>
39+
<PathTooltip
40+
content={formatPathTooltip(
41+
file.path,
42+
file.lineSnippet ? ` ${file.lineSnippet}` : undefined,
43+
)}>
44+
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
45+
{formatPathTooltip(
46+
file.path,
47+
file.lineSnippet ? ` ${file.lineSnippet}` : undefined,
48+
)}
49+
</span>
50+
</PathTooltip>
4251
<div className="flex-grow"></div>
4352
<span className="codicon codicon-link-external text-[13.5px] my-[1px]" />
4453
</ToolUseBlockHeader>

webview-ui/src/components/chat/ChatRow.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { safeJsonParse } from "@roo/safeJsonParse"
1414
import { useExtensionState } from "@src/context/ExtensionStateContext"
1515
import { findMatchingResourceOrTemplate } from "@src/utils/mcp"
1616
import { vscode } from "@src/utils/vscode"
17-
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
17+
import { formatPathTooltip } from "@src/utils/formatPathTooltip"
1818
import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
1919

2020
import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
@@ -61,6 +61,7 @@ import {
6161
MessageCircle,
6262
} from "lucide-react"
6363
import { cn } from "@/lib/utils"
64+
import { PathTooltip } from "../ui/PathTooltip"
6465

6566
interface ChatRowProps {
6667
message: ClineMessage
@@ -552,10 +553,11 @@ export const ChatRowContent = ({
552553
className="group"
553554
onClick={() => vscode.postMessage({ type: "openFile", text: tool.content })}>
554555
{tool.path?.startsWith(".") && <span>.</span>}
555-
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
556-
{removeLeadingNonAlphanumeric(tool.path ?? "") + "\u200E"}
557-
{tool.reason}
558-
</span>
556+
<PathTooltip content={formatPathTooltip(tool.path, tool.reason)}>
557+
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
558+
{formatPathTooltip(tool.path, tool.reason)}
559+
</span>
560+
</PathTooltip>
559561
<div style={{ flexGrow: 1 }}></div>
560562
<SquareArrowOutUpRight
561563
className="w-4 shrink-0 codicon codicon-link-external opacity-0 group-hover:opacity-100 transition-opacity"

webview-ui/src/components/common/CodeAccordian.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ import { memo, useMemo } from "react"
22
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
33
import { type ToolProgressStatus } from "@roo-code/types"
44
import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
5-
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
5+
import { formatPathTooltip } from "@src/utils/formatPathTooltip"
66

77
import { ToolUseBlock, ToolUseBlockHeader } from "./ToolUseBlock"
88
import CodeBlock from "./CodeBlock"
9+
import { PathTooltip } from "../ui/PathTooltip"
910

1011
interface CodeAccordianProps {
1112
path?: string
@@ -44,7 +45,9 @@ const CodeAccordian = ({
4445
{header ? (
4546
<div className="flex items-center">
4647
<span className="codicon codicon-server mr-1.5"></span>
47-
<span className="whitespace-nowrap overflow-hidden text-ellipsis mr-2">{header}</span>
48+
<PathTooltip content={header}>
49+
<span className="whitespace-nowrap overflow-hidden text-ellipsis mr-2">{header}</span>
50+
</PathTooltip>
4851
</div>
4952
) : isFeedback ? (
5053
<div className="flex items-center">
@@ -56,9 +59,11 @@ const CodeAccordian = ({
5659
) : (
5760
<>
5861
{path?.startsWith(".") && <span>.</span>}
59-
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
60-
{removeLeadingNonAlphanumeric(path ?? "") + "\u200E"}
61-
</span>
62+
<PathTooltip content={formatPathTooltip(path)}>
63+
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
64+
{formatPathTooltip(path)}
65+
</span>
66+
</PathTooltip>
6267
</>
6368
)}
6469
<div className="flex-grow-1" />
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { ReactNode } from "react"
2+
import { StandardTooltip } from "./standard-tooltip"
3+
4+
interface PathTooltipProps {
5+
/** The file path or content to display in the tooltip */
6+
content: string
7+
/** The element(s) that trigger the tooltip */
8+
children: ReactNode
9+
/** The preferred side of the trigger to render the tooltip */
10+
side?: "top" | "right" | "bottom" | "left"
11+
/** The preferred alignment against the trigger */
12+
align?: "start" | "center" | "end"
13+
/** Distance in pixels from the trigger */
14+
sideOffset?: number
15+
/** Whether the trigger should be rendered as a child */
16+
asChild?: boolean
17+
}
18+
19+
/**
20+
* PathTooltip component specifically designed for displaying file paths with appropriate
21+
* wrapping and responsive width behavior. Use this for truncated file paths that need
22+
* tooltips to show the full path.
23+
*
24+
* Features:
25+
* - Responsive max-width that adapts to panel size (max 300px on wide panels, 100vw on narrow)
26+
* - Uses text-wrap instead of text-balance to minimize whitespace
27+
* - Leverages existing break-words CSS for natural line breaking at path separators
28+
*
29+
* @example
30+
* <PathTooltip content="/very/long/file/path.tsx">
31+
* <span className="truncate">...path.tsx</span>
32+
* </PathTooltip>
33+
*/
34+
export function PathTooltip({
35+
content,
36+
children,
37+
side = "top",
38+
align = "start",
39+
sideOffset = 4,
40+
asChild = true,
41+
}: PathTooltipProps) {
42+
return (
43+
<StandardTooltip
44+
content={content}
45+
side={side}
46+
align={align}
47+
sideOffset={sideOffset}
48+
className="[text-wrap:wrap]"
49+
maxWidth="min(300px,100vw)"
50+
asChild={asChild}>
51+
{children}
52+
</StandardTooltip>
53+
)
54+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { removeLeadingNonAlphanumeric } from "./removeLeadingNonAlphanumeric"
2+
3+
/**
4+
* Formats a file path for display in tooltips with consistent formatting.
5+
*
6+
* @param path - The file path to format
7+
* @param additionalContent - Optional additional content to append (e.g., line snippets, reasons)
8+
* @returns Formatted string ready for tooltip display
9+
*
10+
* @example
11+
* formatPathTooltip("/src/components/MyComponent.tsx")
12+
* // Returns: "src/components/MyComponent.tsx" + U+200E
13+
*
14+
* @example
15+
* formatPathTooltip("/src/utils/helper.ts", ":42-45")
16+
* // Returns: "src/utils/helper.ts:42-45" + U+200E
17+
*/
18+
export function formatPathTooltip(path?: string, additionalContent?: string): string {
19+
if (!path) return ""
20+
21+
const formattedPath = removeLeadingNonAlphanumeric(path) + "\u200E"
22+
23+
if (additionalContent) {
24+
return formattedPath + additionalContent
25+
}
26+
27+
return formattedPath
28+
}

0 commit comments

Comments
 (0)