Skip to content

Commit 904e956

Browse files
feat: add persistent version indicator on chat screen (RooCodeInc#5115) (RooCodeInc#5128)
Co-authored-by: Daniel Riccio <[email protected]>
1 parent 000a9e5 commit 904e956

File tree

21 files changed

+209
-1
lines changed

21 files changed

+209
-1
lines changed

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import RooTips from "@src/components/welcome/RooTips"
3333
import { StandardTooltip } from "@src/components/ui"
3434

3535
import TelemetryBanner from "../common/TelemetryBanner"
36+
import VersionIndicator from "../common/VersionIndicator"
3637
import { useTaskSearch } from "../history/useTaskSearch"
3738
import HistoryPreview from "../history/HistoryPreview"
3839
import Announcement from "./Announcement"
@@ -151,6 +152,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
151152
const [wasStreaming, setWasStreaming] = useState<boolean>(false)
152153
const [showCheckpointWarning, setShowCheckpointWarning] = useState<boolean>(false)
153154
const [isCondensing, setIsCondensing] = useState<boolean>(false)
155+
const [showAnnouncementModal, setShowAnnouncementModal] = useState(false)
154156
const everVisibleMessagesTsRef = useRef<LRUCache<number, boolean>>(
155157
new LRUCache({
156158
max: 250,
@@ -1365,7 +1367,21 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
13651367

13661368
return (
13671369
<div className={isHidden ? "hidden" : "fixed top-0 left-0 right-0 bottom-0 flex flex-col overflow-hidden"}>
1368-
{showAnnouncement && <Announcement hideAnnouncement={hideAnnouncement} />}
1370+
{/* Version indicator in top-right corner */}
1371+
<VersionIndicator onClick={() => setShowAnnouncementModal(true)} className="absolute top-2 right-3 z-10" />
1372+
1373+
{(showAnnouncement || showAnnouncementModal) && (
1374+
<Announcement
1375+
hideAnnouncement={() => {
1376+
if (showAnnouncementModal) {
1377+
setShowAnnouncementModal(false)
1378+
}
1379+
if (showAnnouncement) {
1380+
hideAnnouncement()
1381+
}
1382+
}}
1383+
/>
1384+
)}
13691385
{task ? (
13701386
<>
13711387
<TaskHeader

webview-ui/src/components/chat/__tests__/ChatView.spec.tsx

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,55 @@ vi.mock("../AutoApproveMenu", () => ({
6161
default: () => null,
6262
}))
6363

64+
vi.mock("@src/components/common/VersionIndicator", () => ({
65+
default: function MockVersionIndicator({ onClick, className }: { onClick: () => void; className?: string }) {
66+
// eslint-disable-next-line @typescript-eslint/no-require-imports
67+
const React = require("react")
68+
return React.createElement(
69+
"button",
70+
{
71+
onClick,
72+
className,
73+
"aria-label": "chat:versionIndicator.ariaLabel",
74+
"data-testid": "version-indicator",
75+
},
76+
"v3.21.5",
77+
)
78+
},
79+
}))
80+
81+
vi.mock("@src/components/modals/Announcement", () => ({
82+
default: function MockAnnouncement({ hideAnnouncement }: { hideAnnouncement: () => void }) {
83+
// eslint-disable-next-line @typescript-eslint/no-require-imports
84+
const React = require("react")
85+
return React.createElement(
86+
"div",
87+
{ "data-testid": "announcement-modal" },
88+
React.createElement("div", null, "What's New"),
89+
React.createElement("button", { onClick: hideAnnouncement }, "Close"),
90+
)
91+
},
92+
}))
93+
94+
// Mock i18n
95+
vi.mock("react-i18next", () => ({
96+
useTranslation: () => ({
97+
t: (key: string, options?: any) => {
98+
if (key === "chat:versionIndicator.ariaLabel" && options?.version) {
99+
return `Version ${options.version}`
100+
}
101+
return key
102+
},
103+
}),
104+
initReactI18next: {
105+
type: "3rdParty",
106+
init: () => {},
107+
},
108+
Trans: ({ i18nKey, children }: { i18nKey: string; children?: React.ReactNode }) => {
109+
return <>{children || i18nKey}</>
110+
},
111+
}))
112+
64113
interface ChatTextAreaProps {
65114
onSend: (value: string) => void
66115
inputValue?: string
@@ -1068,3 +1117,69 @@ describe("ChatView - Focus Grabbing Tests", () => {
10681117
expect(mockFocus).toHaveBeenCalledTimes(FOCUS_CALLS_ON_INIT)
10691118
})
10701119
})
1120+
1121+
describe("ChatView - Version Indicator Tests", () => {
1122+
beforeEach(() => vi.clearAllMocks())
1123+
1124+
it("displays version indicator button", () => {
1125+
const { getByLabelText } = renderChatView()
1126+
1127+
// First hydrate state
1128+
mockPostMessage({
1129+
clineMessages: [],
1130+
})
1131+
1132+
// Check that version indicator is displayed
1133+
const versionButton = getByLabelText(/version/i)
1134+
expect(versionButton).toBeInTheDocument()
1135+
expect(versionButton).toHaveTextContent(/^v\d+\.\d+\.\d+/)
1136+
})
1137+
1138+
it("opens announcement modal when version indicator is clicked", () => {
1139+
const { container } = renderChatView()
1140+
1141+
// First hydrate state
1142+
mockPostMessage({
1143+
clineMessages: [],
1144+
})
1145+
1146+
// Find version indicator
1147+
const versionButton = container.querySelector('button[aria-label*="version"]') as HTMLButtonElement
1148+
expect(versionButton).toBeTruthy()
1149+
1150+
// Click should trigger modal - we'll just verify the button exists and is clickable
1151+
// The actual modal rendering is handled by the component state
1152+
expect(versionButton.onclick).toBeDefined()
1153+
})
1154+
1155+
it("version indicator has correct styling classes", () => {
1156+
const { getByTestId } = renderChatView()
1157+
1158+
// First hydrate state
1159+
mockPostMessage({
1160+
clineMessages: [],
1161+
})
1162+
1163+
// Check styling classes - the VersionIndicator component receives className prop
1164+
const versionButton = getByTestId("version-indicator")
1165+
expect(versionButton).toBeInTheDocument()
1166+
// The className is passed as a prop to VersionIndicator
1167+
expect(versionButton.className).toContain("absolute top-2 right-3 z-10")
1168+
})
1169+
1170+
it("version indicator has proper accessibility attributes", () => {
1171+
const { container } = renderChatView()
1172+
1173+
// First hydrate state
1174+
mockPostMessage({
1175+
clineMessages: [],
1176+
})
1177+
1178+
// Check accessibility - find button by its content
1179+
const versionButton = container.querySelector('button[aria-label*="version"]')
1180+
expect(versionButton).toBeTruthy()
1181+
expect(versionButton).toHaveAttribute("aria-label")
1182+
// The mock returns the key, so we check for that
1183+
expect(versionButton?.getAttribute("aria-label")).toBe("chat:versionIndicator.ariaLabel")
1184+
})
1185+
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from "react"
2+
import { useTranslation } from "react-i18next"
3+
import { Package } from "@roo/package"
4+
5+
interface VersionIndicatorProps {
6+
onClick: () => void
7+
className?: string
8+
}
9+
10+
const VersionIndicator: React.FC<VersionIndicatorProps> = ({ onClick, className = "" }) => {
11+
const { t } = useTranslation()
12+
13+
return (
14+
<button
15+
onClick={onClick}
16+
className={`text-xs text-vscode-descriptionForeground hover:text-vscode-foreground transition-colors cursor-pointer px-2 py-1 rounded border border-vscode-panel-border hover:border-vscode-focusBorder ${className}`}
17+
aria-label={t("chat:versionIndicator.ariaLabel", { version: Package.version })}>
18+
v{Package.version}
19+
</button>
20+
)
21+
}
22+
23+
export default VersionIndicator

webview-ui/src/i18n/locales/ca/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,8 @@
310310
"indexed": "Indexat",
311311
"error": "Error d'índex",
312312
"status": "Estat de l'índex"
313+
},
314+
"versionIndicator": {
315+
"ariaLabel": "Versió {{version}} - Feu clic per veure les notes de llançament"
313316
}
314317
}

webview-ui/src/i18n/locales/de/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,8 @@
310310
"indexed": "Indiziert",
311311
"error": "Index-Fehler",
312312
"status": "Index-Status"
313+
},
314+
"versionIndicator": {
315+
"ariaLabel": "Version {{version}} - Klicken Sie, um die Versionshinweise anzuzeigen"
313316
}
314317
}

webview-ui/src/i18n/locales/en/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,8 @@
310310
"indexed": "Indexed",
311311
"error": "Index error",
312312
"status": "Index status"
313+
},
314+
"versionIndicator": {
315+
"ariaLabel": "Version {{version}} - Click to view release notes"
313316
}
314317
}

webview-ui/src/i18n/locales/es/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,8 @@
310310
"indexed": "Indexado",
311311
"error": "Error de índice",
312312
"status": "Estado del índice"
313+
},
314+
"versionIndicator": {
315+
"ariaLabel": "Versión {{version}} - Haz clic para ver las notas de la versión"
313316
}
314317
}

webview-ui/src/i18n/locales/fr/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,8 @@
310310
"indexed": "Indexé",
311311
"error": "Erreur d'index",
312312
"status": "Statut de l'index"
313+
},
314+
"versionIndicator": {
315+
"ariaLabel": "Version {{version}} - Cliquez pour voir les notes de version"
313316
}
314317
}

webview-ui/src/i18n/locales/hi/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,5 +310,8 @@
310310
"indexed": "इंडेक्स किया गया",
311311
"error": "इंडेक्स त्रुटि",
312312
"status": "इंडेक्स स्थिति"
313+
},
314+
"versionIndicator": {
315+
"ariaLabel": "संस्करण {{version}} - रिलीज़ नोट्स देखने के लिए क्लिक करें"
313316
}
314317
}

webview-ui/src/i18n/locales/id/chat.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,5 +316,8 @@
316316
"indexed": "Terindeks",
317317
"error": "Error indeks",
318318
"status": "Status indeks"
319+
},
320+
"versionIndicator": {
321+
"ariaLabel": "Versi {{version}} - Klik untuk melihat catatan rilis"
319322
}
320323
}

0 commit comments

Comments
 (0)