Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions webview-ui/src/components/chat/BatchFilePermission.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { memo } from "react"

import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
import { vscode } from "@src/utils/vscode"
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
import { formatPathTooltip } from "@src/utils/formatPathTooltip"
import { PathTooltip } from "../ui/PathTooltip"

interface FilePermissionItem {
path: string
Expand Down Expand Up @@ -35,10 +36,18 @@ export const BatchFilePermission = memo(({ files = [], onPermissionResponse, ts
<ToolUseBlockHeader
onClick={() => vscode.postMessage({ type: "openFile", text: file.content })}>
{file.path?.startsWith(".") && <span>.</span>}
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{removeLeadingNonAlphanumeric(file.path ?? "") + "\u200E"}
{file.lineSnippet && ` ${file.lineSnippet}`}
</span>
<PathTooltip
content={formatPathTooltip(
file.path,
file.lineSnippet ? ` ${file.lineSnippet}` : undefined,
)}>
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{formatPathTooltip(
file.path,
file.lineSnippet ? ` ${file.lineSnippet}` : undefined,
)}
</span>
</PathTooltip>
<div className="flex-grow"></div>
<span className="codicon codicon-link-external text-[13.5px] my-[1px]" />
</ToolUseBlockHeader>
Expand Down
12 changes: 7 additions & 5 deletions webview-ui/src/components/chat/ChatRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { safeJsonParse } from "@roo/safeJsonParse"
import { useExtensionState } from "@src/context/ExtensionStateContext"
import { findMatchingResourceOrTemplate } from "@src/utils/mcp"
import { vscode } from "@src/utils/vscode"
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
import { formatPathTooltip } from "@src/utils/formatPathTooltip"
import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"

import { ToolUseBlock, ToolUseBlockHeader } from "../common/ToolUseBlock"
Expand Down Expand Up @@ -61,6 +61,7 @@ import {
MessageCircle,
} from "lucide-react"
import { cn } from "@/lib/utils"
import { PathTooltip } from "../ui/PathTooltip"

interface ChatRowProps {
message: ClineMessage
Expand Down Expand Up @@ -552,10 +553,11 @@ export const ChatRowContent = ({
className="group"
onClick={() => vscode.postMessage({ type: "openFile", text: tool.content })}>
{tool.path?.startsWith(".") && <span>.</span>}
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{removeLeadingNonAlphanumeric(tool.path ?? "") + "\u200E"}
{tool.reason}
</span>
<PathTooltip content={formatPathTooltip(tool.path, tool.reason)}>
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{formatPathTooltip(tool.path, tool.reason)}
</span>
</PathTooltip>
<div style={{ flexGrow: 1 }}></div>
<SquareArrowOutUpRight
className="w-4 shrink-0 codicon codicon-link-external opacity-0 group-hover:opacity-100 transition-opacity"
Expand Down
15 changes: 10 additions & 5 deletions webview-ui/src/components/common/CodeAccordian.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { memo, useMemo } from "react"
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
import { type ToolProgressStatus } from "@roo-code/types"
import { getLanguageFromPath } from "@src/utils/getLanguageFromPath"
import { removeLeadingNonAlphanumeric } from "@src/utils/removeLeadingNonAlphanumeric"
import { formatPathTooltip } from "@src/utils/formatPathTooltip"

import { ToolUseBlock, ToolUseBlockHeader } from "./ToolUseBlock"
import CodeBlock from "./CodeBlock"
import { PathTooltip } from "../ui/PathTooltip"

interface CodeAccordianProps {
path?: string
Expand Down Expand Up @@ -44,7 +45,9 @@ const CodeAccordian = ({
{header ? (
<div className="flex items-center">
<span className="codicon codicon-server mr-1.5"></span>
<span className="whitespace-nowrap overflow-hidden text-ellipsis mr-2">{header}</span>
<PathTooltip content={header}>
<span className="whitespace-nowrap overflow-hidden text-ellipsis mr-2">{header}</span>
</PathTooltip>
</div>
) : isFeedback ? (
<div className="flex items-center">
Expand All @@ -56,9 +59,11 @@ const CodeAccordian = ({
) : (
<>
{path?.startsWith(".") && <span>.</span>}
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{removeLeadingNonAlphanumeric(path ?? "") + "\u200E"}
</span>
<PathTooltip content={formatPathTooltip(path)}>
<span className="whitespace-nowrap overflow-hidden text-ellipsis text-left mr-2 rtl">
{formatPathTooltip(path)}
</span>
</PathTooltip>
</>
)}
<div className="flex-grow-1" />
Expand Down
54 changes: 54 additions & 0 deletions webview-ui/src/components/ui/PathTooltip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { ReactNode } from "react"
import { StandardTooltip } from "./standard-tooltip"

interface PathTooltipProps {
/** The file path or content to display in the tooltip */
content: string
/** The element(s) that trigger the tooltip */
children: ReactNode
/** The preferred side of the trigger to render the tooltip */
side?: "top" | "right" | "bottom" | "left"
/** The preferred alignment against the trigger */
align?: "start" | "center" | "end"
/** Distance in pixels from the trigger */
sideOffset?: number
/** Whether the trigger should be rendered as a child */
asChild?: boolean
}

/**
* PathTooltip component specifically designed for displaying file paths with appropriate
* wrapping and responsive width behavior. Use this for truncated file paths that need
* tooltips to show the full path.
*
* Features:
* - Responsive max-width that adapts to panel size (max 300px on wide panels, 100vw on narrow)
* - Uses text-wrap instead of text-balance to minimize whitespace
* - Leverages existing break-words CSS for natural line breaking at path separators
*
* @example
* <PathTooltip content="/very/long/file/path.tsx">
* <span className="truncate">...path.tsx</span>
* </PathTooltip>
*/
export function PathTooltip({
content,
children,
side = "top",
align = "start",
sideOffset = 4,
asChild = true,
}: PathTooltipProps) {
return (
<StandardTooltip
content={content}
side={side}
align={align}
sideOffset={sideOffset}
className="[text-wrap:wrap]"
maxWidth="min(300px,100vw)"
asChild={asChild}>
{children}
</StandardTooltip>
)
}
28 changes: 28 additions & 0 deletions webview-ui/src/utils/formatPathTooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { removeLeadingNonAlphanumeric } from "./removeLeadingNonAlphanumeric"

/**
* Formats a file path for display in tooltips with consistent formatting.
*
* @param path - The file path to format
* @param additionalContent - Optional additional content to append (e.g., line snippets, reasons)
* @returns Formatted string ready for tooltip display
*
* @example
* formatPathTooltip("/src/components/MyComponent.tsx")
* // Returns: "src/components/MyComponent.tsx" + U+200E
*
* @example
* formatPathTooltip("/src/utils/helper.ts", ":42-45")
* // Returns: "src/utils/helper.ts:42-45" + U+200E
*/
export function formatPathTooltip(path?: string, additionalContent?: string): string {
if (!path) return ""

const formattedPath = removeLeadingNonAlphanumeric(path) + "\u200E"

if (additionalContent) {
return formattedPath + additionalContent
}

return formattedPath
}