Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
efaaed9
feat: add Issue Fixer Orchestrator mode
MuriloFP Jul 3, 2025
57d3fbe
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 3, 2025
ef61905
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 4, 2025
f5a51c4
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 4, 2025
bcbf329
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 5, 2025
80413c0
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 5, 2025
ab10140
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 7, 2025
39c5cf7
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 7, 2025
00a0b63
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 8, 2025
080b61b
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 8, 2025
7a5ad14
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 8, 2025
2c73ff2
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 9, 2025
05ccf57
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 10, 2025
fdb1f35
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 11, 2025
10ce509
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 14, 2025
ab1f9fc
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 15, 2025
74fd8b4
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 15, 2025
6745c8f
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 16, 2025
faf2ee5
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 17, 2025
b2dadf9
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 17, 2025
f648e4c
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 17, 2025
a6d1e60
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 21, 2025
be90907
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 21, 2025
ed3a077
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 22, 2025
856313f
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 24, 2025
4dd68ea
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 28, 2025
b10fa5e
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 29, 2025
f016d7b
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 30, 2025
23855f2
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 31, 2025
3803c29
Merge branch 'RooCodeInc:main' into main
MuriloFP Jul 31, 2025
f258f49
feat: Add mode display indicators on task cards (#6493)
MuriloFP Jul 31, 2025
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
6 changes: 6 additions & 0 deletions webview-ui/src/components/chat/TaskHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { useExtensionState } from "@src/context/ExtensionStateContext"
import { useSelectedModel } from "@/components/ui/hooks/useSelectedModel"

import Thumbnails from "../common/Thumbnails"
import { ModeBadge } from "../common/ModeBadge"

import { TaskActions } from "./TaskActions"
import { ShareButton } from "./ShareButton"
Expand Down Expand Up @@ -101,6 +102,11 @@ const TaskHeader = ({
</span>
)}
</div>
{currentTaskItem?.mode && (
<div className="ml-2 flex-shrink-0">
<ModeBadge modeSlug={currentTaskItem.mode} />
</div>
)}
</div>
<StandardTooltip content={t("chat:task.closeAndStart")}>
<Button variant="ghost" size="icon" onClick={onClose} className="shrink-0 w-5 h-5">
Expand Down
42 changes: 42 additions & 0 deletions webview-ui/src/components/common/ModeBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react"
import { getModeBySlug } from "@roo/modes"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import path inconsistency detected. This uses while the rest of the codebase uses or patterns. Could we update this to match the project's import conventions?

import { Badge } from "@/components/ui/badge"
import { StandardTooltip } from "@/components/ui"
import { cn } from "@/lib/utils"
import { useExtensionState } from "@/context/ExtensionStateContext"

interface ModeBadgeProps {
modeSlug: string | undefined
className?: string
}

export const ModeBadge: React.FC<ModeBadgeProps> = ({ modeSlug, className }) => {
const { customModes } = useExtensionState()

if (!modeSlug) {
return null
}

const mode = getModeBySlug(modeSlug, customModes)

// If mode is not found (e.g., deleted custom mode), show the slug as fallback
const displayName = mode?.name || modeSlug
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback to showing the raw slug might be confusing for users. Could we consider a more user-friendly fallback like "Unknown mode" or hide the badge entirely for deleted modes?


// Truncate long mode names
const truncatedName = displayName.length > 20 ? `${displayName.substring(0, 17)}...` : displayName
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hard-coded truncation logic (20 characters, 17 + "...") might not work well for all languages or use cases. Could we consider making this configurable or using CSS for better performance and consistency?


return (
<StandardTooltip content={displayName}>
<Badge
variant="outline"
className={cn(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The styling overrides here conflict with the Badge variant's default styling. This could break visual consistency across the app. Is there a way to work with the existing Badge variants instead of overriding them?

"text-xs font-normal px-1.5 py-0 h-5",
"bg-vscode-badge-background text-vscode-badge-foreground",
"border-vscode-badge-background",
className,
)}>
{truncatedName}
</Badge>
</StandardTooltip>
)
}
172 changes: 172 additions & 0 deletions webview-ui/src/components/common/__tests__/ModeBadge.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { render, screen } from "@testing-library/react"
import { ModeBadge } from "../ModeBadge"
import { getModeBySlug } from "@roo/modes"
import { useExtensionState } from "@/context/ExtensionStateContext"

// Mock dependencies
vi.mock("@roo/modes")
vi.mock("@/context/ExtensionStateContext")
vi.mock("@/components/ui", () => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mocking the entire module could be brittle. Could we consider more targeted mocks for just the components we need to test?

StandardTooltip: ({ children, content }: any) => <div title={content}>{children}</div>,
Badge: ({ children, className }: any) => <div className={className}>{children}</div>,
}))

const mockGetModeBySlug = vi.mocked(getModeBySlug)
const mockUseExtensionState = vi.mocked(useExtensionState)

describe("ModeBadge", () => {
beforeEach(() => {
vi.clearAllMocks()
mockUseExtensionState.mockReturnValue({
customModes: [],
} as any)
})

it("should not render when modeSlug is undefined", () => {
const { container } = render(<ModeBadge modeSlug={undefined} />)
expect(container.firstChild).toBeNull()
})

it("should render mode name for built-in mode", () => {
mockGetModeBySlug.mockReturnValue({
slug: "code",
name: "💻 Code",
roleDefinition: "You are a code assistant",
groups: ["read", "edit"] as const,
} as any)

render(<ModeBadge modeSlug="code" />)

expect(screen.getByText("💻 Code")).toBeInTheDocument()
expect(mockGetModeBySlug).toHaveBeenCalledWith("code", [])
})

it("should render mode name for custom mode", () => {
const customModes = [
{
slug: "custom-mode",
name: "🎨 Custom Mode",
roleDefinition: "Custom role",
groups: ["read"] as const,
},
]

mockUseExtensionState.mockReturnValue({
customModes,
} as any)

mockGetModeBySlug.mockReturnValue(customModes[0] as any)

render(<ModeBadge modeSlug="custom-mode" />)

expect(screen.getByText("🎨 Custom Mode")).toBeInTheDocument()
expect(mockGetModeBySlug).toHaveBeenCalledWith("custom-mode", customModes)
})

it("should render mode slug as fallback for deleted custom mode", () => {
mockGetModeBySlug.mockReturnValue(undefined)

render(<ModeBadge modeSlug="deleted-mode" />)

expect(screen.getByText("deleted-mode")).toBeInTheDocument()
})

it("should truncate long mode names", () => {
mockGetModeBySlug.mockReturnValue({
slug: "long-mode",
name: "This is a very long mode name that should be truncated",
roleDefinition: "Long mode",
groups: ["read"] as const,
} as any)

render(<ModeBadge modeSlug="long-mode" />)

expect(screen.getByText("This is a very lo...")).toBeInTheDocument()
})

it("should show full name in tooltip", () => {
const longName = "This is a very long mode name that should be truncated"
mockGetModeBySlug.mockReturnValue({
slug: "long-mode",
name: longName,
roleDefinition: "Long mode",
groups: ["read"] as const,
} as any)

render(<ModeBadge modeSlug="long-mode" />)

// The StandardTooltip component should have the full name as content
const badge = screen.getByText("This is a very lo...")
expect(badge.closest("[title]")).toHaveAttribute("title", longName)
})

it("should apply custom className", () => {
mockGetModeBySlug.mockReturnValue({
slug: "code",
name: "Code",
roleDefinition: "Code mode",
groups: ["read"] as const,
} as any)

render(<ModeBadge modeSlug="code" className="custom-class" />)

const badge = screen.getByText("Code")
expect(badge).toHaveClass("custom-class")
})

it("should handle mode without emoji", () => {
mockGetModeBySlug.mockReturnValue({
slug: "plain-mode",
name: "Plain Mode",
roleDefinition: "Plain mode",
groups: ["read"] as const,
} as any)

render(<ModeBadge modeSlug="plain-mode" />)

expect(screen.getByText("Plain Mode")).toBeInTheDocument()
})

it("should use correct styling classes", () => {
mockGetModeBySlug.mockReturnValue({
slug: "test-mode",
name: "Test Mode",
roleDefinition: "Test mode",
groups: ["read"] as const,
} as any)

render(<ModeBadge modeSlug="test-mode" />)

const badge = screen.getByText("Test Mode")
expect(badge).toHaveClass("bg-vscode-badge-background")
expect(badge).toHaveClass("text-vscode-badge-foreground")
expect(badge).toHaveClass("border-vscode-badge-background")
expect(badge).toHaveClass("text-xs")
expect(badge).toHaveClass("font-normal")
expect(badge).toHaveClass("px-1.5")
expect(badge).toHaveClass("py-0")
expect(badge).toHaveClass("h-5")
})

it("should handle all built-in modes", () => {
const builtInModes = [
{ slug: "architect", name: "🏗️ Architect" },
{ slug: "code", name: "💻 Code" },
{ slug: "ask", name: "❓ Ask" },
{ slug: "debug", name: "🪲 Debug" },
{ slug: "test", name: "🧪 Test" },
]

builtInModes.forEach((mode) => {
mockGetModeBySlug.mockReturnValue({
...mode,
roleDefinition: "Test",
groups: ["read"] as const,
} as any)

const { unmount } = render(<ModeBadge modeSlug={mode.slug} />)
expect(screen.getByText(mode.name)).toBeInTheDocument()
unmount()
})
})
})
2 changes: 2 additions & 0 deletions webview-ui/src/components/history/TaskItemHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { HistoryItem } from "@roo-code/types"
import { formatDate } from "@/utils/format"
import { DeleteButton } from "./DeleteButton"
import { cn } from "@/lib/utils"
import { ModeBadge } from "@/components/common/ModeBadge"

export interface TaskItemHeaderProps {
item: HistoryItem
Expand All @@ -22,6 +23,7 @@ const TaskItemHeader: React.FC<TaskItemHeaderProps> = ({ item, isSelectionMode,
<span className="text-vscode-descriptionForeground font-medium text-sm uppercase">
{formatDate(item.ts)}
</span>
{item.mode && <ModeBadge modeSlug={item.mode} />}
</div>

{/* Action Buttons */}
Expand Down