Ver Fonte

Roo Code Cloud Waitlist CTAs (#6104)

Co-authored-by: Roo Code <[email protected]>
Co-authored-by: Bruno Bergher <[email protected]>
Bruno Bergher há 5 meses atrás
pai
commit
4f8c9688a2

+ 6 - 0
apps/web-roo-code/next.config.ts

@@ -21,6 +21,12 @@ const nextConfig: NextConfig = {
 				destination: "https://roocode.com/:path*",
 				permanent: true,
 			},
+			// Redirect cloud waitlist to Notion page
+			{
+				source: "/cloud-waitlist",
+				destination: "https://shard-dogwood-daf.notion.site/238fd1401b0a8087b858e1ad431507cf?pvs=105",
+				permanent: false,
+			},
 		]
 	},
 }

+ 28 - 9
apps/web-roo-code/src/components/chromes/nav-bar.tsx

@@ -61,18 +61,11 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 						className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
 						Enterprise
 					</Link>
-					<a
-						href={EXTERNAL_LINKS.SECURITY}
-						target="_blank"
-						rel="noopener noreferrer"
-						className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
-						Security
-					</a>
 					<a
 						href={EXTERNAL_LINKS.DOCUMENTATION}
 						target="_blank"
 						className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
-						Documentation
+						Docs
 					</a>
 					<a
 						href={EXTERNAL_LINKS.CAREERS}
@@ -80,6 +73,19 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 						className="text-muted-foreground transition-transform duration-200 hover:scale-105 hover:text-foreground">
 						Careers
 					</a>
+					<div className="flex items-center rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 p-0.5 text-xs">
+						<div className="rounded-full bg-background px-2 py-1.5">
+							<span className="text-muted-foreground border-r-2 border-foreground/50 pr-1.5">
+								Roo Code Cloud is coming
+							</span>
+							<a
+								href="/cloud-waitlist"
+								rel="noopener noreferrer"
+								className="font-medium text-primary hover:underline pl-1.5">
+								Sign up
+							</a>
+						</div>
+					</div>
 				</nav>
 
 				<div className="hidden md:flex md:items-center md:space-x-4">
@@ -119,6 +125,19 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 			<div
 				className={`absolute left-0 right-0 top-16 z-50 transform border-b border-border bg-background shadow-lg backdrop-blur-none transition-all duration-200 md:hidden ${isMenuOpen ? "translate-y-0 opacity-100" : "pointer-events-none -translate-y-2 opacity-0"}`}>
 				<nav className="flex flex-col py-2">
+					<div className="mx-5 mb-2 flex items-center rounded-full bg-gradient-to-r from-blue-400 to-cyan-400 p-0.5 text-xs">
+						<div className="flex-grow text-center rounded-full bg-background px-2 py-1.5">
+							<span className="text-muted-foreground border-r-2 border-foreground/50 pr-3">
+								Roo Code Cloud is coming
+							</span>
+							<a
+								href="/cloud-waitlist"
+								rel="noopener noreferrer"
+								className="font-medium text-primary hover:underline pl-3">
+								Sign up
+							</a>
+						</div>
+					</div>
 					<ScrollButton
 						targetId="features"
 						className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
@@ -162,7 +181,7 @@ export function NavBar({ stars, downloads }: NavBarProps) {
 						target="_blank"
 						className="w-full px-8 py-3 text-left text-sm font-medium text-foreground/80 transition-colors hover:bg-accent hover:text-foreground"
 						onClick={() => setIsMenuOpen(false)}>
-						Documentation
+						Docs
 					</a>
 					<a
 						href={EXTERNAL_LINKS.CAREERS}

+ 4 - 15
webview-ui/src/components/chat/ChatView.tsx

@@ -3,7 +3,6 @@ import { useDeepCompareEffect, useEvent, useMount } from "react-use"
 import debounce from "debounce"
 import { Virtuoso, type VirtuosoHandle } from "react-virtuoso"
 import removeMd from "remove-markdown"
-import { Trans } from "react-i18next"
 import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
 import useSound from "use-sound"
 import { LRUCache } from "lru-cache"
@@ -32,12 +31,12 @@ import {
 	parseCommand,
 } from "@src/utils/command-validation"
 import { useTranslation } from "react-i18next"
-import { buildDocLink } from "@src/utils/docLinks"
 import { useAppTranslation } from "@src/i18n/TranslationContext"
 import { useExtensionState } from "@src/context/ExtensionStateContext"
 import { useSelectedModel } from "@src/components/ui/hooks/useSelectedModel"
 import RooHero from "@src/components/welcome/RooHero"
 import RooTips from "@src/components/welcome/RooTips"
+import RooCloudCTA from "@src/components/welcome/RooCloudCTA"
 import { StandardTooltip } from "@src/components/ui"
 import { useAutoApprovalState } from "@src/hooks/useAutoApprovalState"
 import { useAutoApprovalToggles } from "@src/hooks/useAutoApprovalToggles"
@@ -115,6 +114,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 		historyPreviewCollapsed, // Added historyPreviewCollapsed
 		soundEnabled,
 		soundVolume,
+		cloudIsAuthenticated,
 	} = useExtensionState()
 
 	const messagesRef = useRef(messages)
@@ -1696,20 +1696,9 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
 
 						<RooHero />
 						{telemetrySetting === "unset" && <TelemetryBanner />}
-						<p className="text-vscode-editor-foreground leading-tight font-vscode-font-family text-center text-balance max-w-[380px] mx-auto my-0">
-							<Trans
-								i18nKey="chat:about"
-								components={{
-									DocsLink: (
-										<a href={buildDocLink("", "welcome")} target="_blank" rel="noopener noreferrer">
-											the docs
-										</a>
-									),
-								}}
-							/>
-						</p>
+
 						<div className="mb-2.5">
-							<RooTips cycle={false} />
+							{cloudIsAuthenticated || taskHistory.length < 4 ? <RooTips /> : <RooCloudCTA />}
 						</div>
 						{/* Show the task history preview if expanded and tasks exist */}
 						{taskHistory.length > 0 && isExpanded && <HistoryPreview />}

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

@@ -85,6 +85,40 @@ vi.mock("@src/components/modals/Announcement", () => ({
 	},
 }))
 
+// Mock RooCloudCTA component
+vi.mock("@src/components/welcome/RooCloudCTA", () => ({
+	default: function MockRooCloudCTA() {
+		return (
+			<div data-testid="roo-cloud-cta">
+				<div>rooCloudCTA.title</div>
+				<div>rooCloudCTA.description</div>
+				<div>rooCloudCTA.joinWaitlist</div>
+			</div>
+		)
+	},
+}))
+
+// Mock RooTips component
+vi.mock("@src/components/welcome/RooTips", () => ({
+	default: function MockRooTips() {
+		return <div data-testid="roo-tips">Tips content</div>
+	},
+}))
+
+// Mock RooHero component
+vi.mock("@src/components/welcome/RooHero", () => ({
+	default: function MockRooHero() {
+		return <div data-testid="roo-hero">Hero content</div>
+	},
+}))
+
+// Mock TelemetryBanner component
+vi.mock("../common/TelemetryBanner", () => ({
+	default: function MockTelemetryBanner() {
+		return null // Don't render anything to avoid interference
+	},
+}))
+
 // Mock i18n
 vi.mock("react-i18next", () => ({
 	useTranslation: () => ({
@@ -191,6 +225,8 @@ const mockPostMessage = (state: Partial<ExtensionState>) => {
 				shouldShowAnnouncement: false,
 				allowedCommands: [],
 				alwaysAllowExecute: false,
+				cloudIsAuthenticated: false,
+				telemetrySetting: "enabled",
 				...state,
 			},
 		},
@@ -1310,3 +1346,164 @@ describe("ChatView - Version Indicator Tests", () => {
 		expect(versionButton).not.toBeInTheDocument()
 	})
 })
+
+describe("ChatView - RooCloudCTA Display Tests", () => {
+	beforeEach(() => vi.clearAllMocks())
+
+	it("does not show RooCloudCTA when user is authenticated to Cloud", () => {
+		const { queryByTestId, getByTestId } = renderChatView()
+
+		// Hydrate state with user authenticated to cloud and some task history
+		mockPostMessage({
+			cloudIsAuthenticated: true,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 4000 },
+				{ id: "2", ts: Date.now() - 3000 },
+				{ id: "3", ts: Date.now() - 2000 },
+				{ id: "4", ts: Date.now() - 1000 },
+				{ id: "5", ts: Date.now() },
+			],
+			clineMessages: [], // No active task
+		})
+
+		// Should not show RooCloudCTA but should show RooTips
+		expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument()
+		expect(getByTestId("roo-tips")).toBeInTheDocument()
+	})
+
+	it("does not show RooCloudCTA when user has only run 3 tasks in their history", () => {
+		const { queryByTestId, getByTestId } = renderChatView()
+
+		// Hydrate state with user not authenticated and only 3 tasks in history
+		mockPostMessage({
+			cloudIsAuthenticated: false,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 2000 },
+				{ id: "2", ts: Date.now() - 1000 },
+				{ id: "3", ts: Date.now() },
+			],
+			clineMessages: [], // No active task
+		})
+
+		// Should not show RooCloudCTA but should show RooTips
+		expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument()
+		expect(getByTestId("roo-tips")).toBeInTheDocument()
+	})
+
+	it("shows RooCloudCTA when user is not authenticated and has run 4 or more tasks", async () => {
+		const { getByTestId, queryByTestId } = renderChatView()
+
+		// Hydrate state with user not authenticated and 4+ tasks in history
+		mockPostMessage({
+			cloudIsAuthenticated: false,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 3000 },
+				{ id: "2", ts: Date.now() - 2000 },
+				{ id: "3", ts: Date.now() - 1000 },
+				{ id: "4", ts: Date.now() },
+			],
+			clineMessages: [], // No active task
+		})
+
+		// Should show RooCloudCTA and not RooTips
+		await waitFor(() => {
+			expect(getByTestId("roo-cloud-cta")).toBeInTheDocument()
+		})
+		expect(queryByTestId("roo-tips")).not.toBeInTheDocument()
+	})
+
+	it("shows RooCloudCTA when user is not authenticated and has run 5 tasks", async () => {
+		const { getByTestId, queryByTestId } = renderChatView()
+
+		// Hydrate state with user not authenticated and 5 tasks in history
+		mockPostMessage({
+			cloudIsAuthenticated: false,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 4000 },
+				{ id: "2", ts: Date.now() - 3000 },
+				{ id: "3", ts: Date.now() - 2000 },
+				{ id: "4", ts: Date.now() - 1000 },
+				{ id: "5", ts: Date.now() },
+			],
+			clineMessages: [], // No active task
+		})
+
+		// Should show RooCloudCTA and not RooTips
+		await waitFor(() => {
+			expect(getByTestId("roo-cloud-cta")).toBeInTheDocument()
+		})
+		expect(queryByTestId("roo-tips")).not.toBeInTheDocument()
+	})
+
+	it("does not show RooCloudCTA when there is an active task (regardless of auth status)", async () => {
+		const { queryByTestId } = renderChatView()
+
+		// Hydrate state with user not authenticated, 4+ tasks, but with an active task
+		mockPostMessage({
+			cloudIsAuthenticated: false,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 3000 },
+				{ id: "2", ts: Date.now() - 2000 },
+				{ id: "3", ts: Date.now() - 1000 },
+				{ id: "4", ts: Date.now() },
+			],
+			clineMessages: [
+				{
+					type: "say",
+					say: "task",
+					ts: Date.now(),
+					text: "Active task in progress",
+				},
+			],
+		})
+
+		// Wait for the state to be updated and the task view to be shown
+		await waitFor(() => {
+			// Should not show RooCloudCTA when there's an active task
+			expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument()
+			// Should not show RooTips either since the entire welcome screen is hidden during active tasks
+			expect(queryByTestId("roo-tips")).not.toBeInTheDocument()
+			// Should not show RooHero either since the entire welcome screen is hidden during active tasks
+			expect(queryByTestId("roo-hero")).not.toBeInTheDocument()
+		})
+	})
+
+	it("shows RooTips when user is authenticated (instead of RooCloudCTA)", () => {
+		const { queryByTestId, getByTestId } = renderChatView()
+
+		// Hydrate state with user authenticated to cloud
+		mockPostMessage({
+			cloudIsAuthenticated: true,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 3000 },
+				{ id: "2", ts: Date.now() - 2000 },
+				{ id: "3", ts: Date.now() - 1000 },
+				{ id: "4", ts: Date.now() },
+			],
+			clineMessages: [], // No active task
+		})
+
+		// Should not show RooCloudCTA but should show RooTips
+		expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument()
+		expect(getByTestId("roo-tips")).toBeInTheDocument()
+	})
+
+	it("shows RooTips when user has fewer than 4 tasks (instead of RooCloudCTA)", () => {
+		const { queryByTestId, getByTestId } = renderChatView()
+
+		// Hydrate state with user not authenticated but fewer than 4 tasks
+		mockPostMessage({
+			cloudIsAuthenticated: false,
+			taskHistory: [
+				{ id: "1", ts: Date.now() - 2000 },
+				{ id: "2", ts: Date.now() - 1000 },
+				{ id: "3", ts: Date.now() },
+			],
+			clineMessages: [], // No active task
+		})
+
+		// Should not show RooCloudCTA but should show RooTips
+		expect(queryByTestId("roo-cloud-cta")).not.toBeInTheDocument()
+		expect(getByTestId("roo-tips")).toBeInTheDocument()
+	})
+})

+ 23 - 0
webview-ui/src/components/welcome/RooCloudCTA.tsx

@@ -0,0 +1,23 @@
+import { useTranslation } from "react-i18next"
+
+export function RooCloudCTA() {
+	const { t } = useTranslation("chat")
+
+	return (
+		<div className="border border-muted/20 px-4 py-1 text-center flex items-start gap-2">
+			<i className="mr-1 codicon codicon-cloud text-xl! mt-2 text-vscode-descriptionForeground" />
+			<div className="text-left">
+				<p>
+					<strong>{t("rooCloudCTA.title")}</strong>
+					<br />
+					<span>{t("rooCloudCTA.description")}</span>
+				</p>
+				<p>
+					<a href="https://roocode.com/cloud-waitlist">{t("rooCloudCTA.joinWaitlist")}</a>
+				</p>
+			</div>
+		</div>
+	)
+}
+
+export default RooCloudCTA

+ 19 - 58
webview-ui/src/components/welcome/RooTips.tsx

@@ -1,7 +1,6 @@
 import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
 import { useTranslation } from "react-i18next"
-import { useState, useEffect } from "react"
-import clsx from "clsx"
+import { Trans } from "react-i18next"
 
 import { buildDocLink } from "@src/utils/docLinks"
 
@@ -20,63 +19,25 @@ const tips = [
 	},
 ]
 
-interface RooTipsProps {
-	cycle?: boolean
-}
-
-const RooTips = ({ cycle = false }: RooTipsProps) => {
+const RooTips = () => {
 	const { t } = useTranslation("chat")
-	const [currentTipIndex, setCurrentTipIndex] = useState(Math.floor(Math.random() * tips.length))
-	const [isFading, setIsFading] = useState(false)
-
-	useEffect(() => {
-		if (!cycle) return
-
-		let timeoutId: NodeJS.Timeout | undefined = undefined
-		const intervalId = setInterval(() => {
-			setIsFading(true) // Start fade out
-			timeoutId = setTimeout(() => {
-				setCurrentTipIndex((prevIndex) => (prevIndex + 1) % tips.length)
-				setIsFading(false) // Start fade in
-			}, 1000) // Fade duration
-		}, 11000) // 10s display + 1s fade
-
-		return () => {
-			clearInterval(intervalId)
-			if (timeoutId) {
-				clearTimeout(timeoutId)
-			}
-		}
-	}, [cycle])
-
-	const currentTip = tips[currentTipIndex]
-	const topTwoTips = tips.slice(0, 2)
 
 	return (
-		<div
-			className={clsx(
-				"flex flex-col items-center justify-center px-5 py-2.5 gap-4",
-				cycle && "h-[5em] overflow-visible m-5",
-			)}>
-			{/* If we need real estate, we show a compressed version of the tips. Otherwise, we expand it. */}
-			{cycle ? (
-				<>
-					<div className="opacity-70 pb-1"> Did you know about...</div>
-					<div
-						className={clsx(
-							"flex items-center gap-2 text-vscode-editor-foreground font-vscode max-w-[250px] transition-opacity duration-1000 ease-in-out",
-							isFading ? "opacity-0" : "opacity-70",
-						)}>
-						{" "}
-						<span className={`codicon ${currentTip.icon}`}></span>
-						<span>
-							<VSCodeLink href={currentTip.href}>{t(currentTip.titleKey)}</VSCodeLink>:{" "}
-							{t(currentTip.descriptionKey)}
-						</span>
-					</div>
-				</>
-			) : (
-				topTwoTips.map((tip) => (
+		<div>
+			<p className="text-vscode-editor-foreground leading-tight font-vscode-font-family text-center text-balance max-w-[380px] mx-auto my-0">
+				<Trans
+					i18nKey="chat:about"
+					components={{
+						DocsLink: (
+							<a href={buildDocLink("", "welcome")} target="_blank" rel="noopener noreferrer">
+								the docs
+							</a>
+						),
+					}}
+				/>
+			</p>
+			<div className="flex flex-col items-center justify-center px-5 py-2.5 gap-4">
+				{tips.map((tip) => (
 					<div
 						key={tip.titleKey}
 						className="flex items-center gap-2 text-vscode-editor-foreground font-vscode max-w-[250px]">
@@ -88,8 +49,8 @@ const RooTips = ({ cycle = false }: RooTipsProps) => {
 							: {t(tip.descriptionKey)}
 						</span>
 					</div>
-				))
-			)}
+				))}
+			</div>
 		</div>
 	)
 }

+ 13 - 3
webview-ui/src/components/welcome/__tests__/RooTips.spec.tsx

@@ -7,6 +7,16 @@ vi.mock("react-i18next", () => ({
 	useTranslation: () => ({
 		t: (key: string) => key, // Simple mock that returns the key
 	}),
+	Trans: ({
+		children,
+		components,
+	}: {
+		children?: React.ReactNode
+		components?: Record<string, React.ReactElement>
+	}) => {
+		// Simple mock that renders children or the first component if no children
+		return children || (components && Object.values(components)[0]) || null
+	},
 }))
 
 vi.mock("@vscode/webview-ui-toolkit/react", () => ({
@@ -25,12 +35,12 @@ describe("RooTips Component", () => {
 
 	describe("when cycle is false (default)", () => {
 		beforeEach(() => {
-			render(<RooTips cycle={false} />)
+			render(<RooTips />)
 		})
 
 		test("renders only the top two tips", () => {
-			// Ensure only two tips are present (check by link role)
-			expect(screen.getAllByRole("link")).toHaveLength(2)
+			// Ensure only two tips are present plus the docs link in the Trans component (3 total links)
+			expect(screen.getAllByRole("link")).toHaveLength(3)
 		})
 	})
 })

+ 5 - 0
webview-ui/src/i18n/locales/ca/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Versió {{version}} - Feu clic per veure les notes de llançament"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud arribarà aviat!",
+		"description": "Executa agents remots al núvol, accedeix a les teves tasques des de qualsevol lloc, col·labora amb altres i molt més.",
+		"joinWaitlist": "Uneix-te a la llista d'espera per obtenir accés anticipat."
+	},
 	"editMessage": {
 		"placeholder": "Edita el teu missatge..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/de/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Version {{version}} - Klicken Sie, um die Versionshinweise anzuzeigen"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud kommt bald!",
+		"description": "Führe Remote-Agenten in der Cloud aus, greife von überall auf deine Aufgaben zu, arbeite mit anderen zusammen und vieles mehr.",
+		"joinWaitlist": "Tritt der Warteliste bei, um frühen Zugang zu erhalten."
+	},
 	"editMessage": {
 		"placeholder": "Bearbeite deine Nachricht..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/en/chat.json

@@ -324,5 +324,10 @@
 	},
 	"versionIndicator": {
 		"ariaLabel": "Version {{version}} - Click to view release notes"
+	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud is coming soon!",
+		"description": "Run Roomote agents in the cloud, access your tasks from anywhere, collaborate with others, and more.",
+		"joinWaitlist": "Join the waitlist to get early access."
 	}
 }

+ 5 - 0
webview-ui/src/i18n/locales/es/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Versión {{version}} - Haz clic para ver las notas de la versión"
 	},
+	"rooCloudCTA": {
+		"title": "¡Roo Code Cloud llegará pronto!",
+		"description": "Ejecuta agentes remotos en la nube, accede a tus tareas desde cualquier lugar, colabora con otros y mucho más.",
+		"joinWaitlist": "Únete a la lista de espera para obtener acceso anticipado."
+	},
 	"editMessage": {
 		"placeholder": "Edita tu mensaje..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/fr/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Version {{version}} - Cliquez pour voir les notes de version"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud arrive bientôt !",
+		"description": "Exécutez des agents distants dans le cloud, accédez à vos tâches de n'importe où, collaborez avec d'autres et bien plus encore.",
+		"joinWaitlist": "Rejoignez la liste d'attente pour obtenir un accès anticipé."
+	},
 	"editMessage": {
 		"placeholder": "Modifiez votre message..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/hi/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "संस्करण {{version}} - रिलीज़ नोट्स देखने के लिए क्लिक करें"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud जल्द आ रहा है!",
+		"description": "क्लाउड में रिमोट एजेंट चलाएं, कहीं से भी अपने कार्यों तक पहुंचें, दूसरों के साथ सहयोग करें, और बहुत कुछ।",
+		"joinWaitlist": "जल्दी पहुंच पाने के लिए प्रतीक्षा सूची में शामिल हों।"
+	},
 	"editMessage": {
 		"placeholder": "अपना संदेश संपादित करें..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/id/chat.json

@@ -329,6 +329,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Versi {{version}} - Klik untuk melihat catatan rilis"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud segera hadir!",
+		"description": "Jalankan agen jarak jauh di cloud, akses tugas Anda dari mana saja, berkolaborasi dengan orang lain, dan banyak lagi.",
+		"joinWaitlist": "Bergabunglah dengan daftar tunggu untuk mendapatkan akses awal."
+	},
 	"editMessage": {
 		"placeholder": "Edit pesan Anda..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/it/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Versione {{version}} - Clicca per visualizzare le note di rilascio"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud arriva presto!",
+		"description": "Esegui agenti remoti nel cloud, accedi alle tue attività da qualsiasi luogo, collabora con altri e molto altro.",
+		"joinWaitlist": "Unisciti alla lista d'attesa per ottenere l'accesso anticipato."
+	},
 	"editMessage": {
 		"placeholder": "Modifica il tuo messaggio..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/ja/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "バージョン {{version}} - クリックしてリリースノートを表示"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud が間もなく登場!",
+		"description": "クラウドでリモートエージェントを実行し、どこからでもタスクにアクセスし、他の人と協力し、その他多くの機能を利用できます。",
+		"joinWaitlist": "早期アクセスを取得するためにウェイトリストに参加してください。"
+	},
 	"editMessage": {
 		"placeholder": "メッセージを編集..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/ko/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "버전 {{version}} - 릴리스 노트를 보려면 클릭하세요"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud가 곧 출시됩니다!",
+		"description": "클라우드에서 원격 에이전트를 실행하고, 어디서나 작업에 액세스하고, 다른 사람들과 협업하는 등 다양한 기능을 이용하세요.",
+		"joinWaitlist": "얼리 액세스를 받으려면 대기 목록에 가입하세요."
+	},
 	"editMessage": {
 		"placeholder": "메시지 편집..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/nl/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Versie {{version}} - Klik om release notes te bekijken"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud komt binnenkort!",
+		"description": "Voer externe agenten uit in de cloud, krijg overal toegang tot je taken, werk samen met anderen en nog veel meer.",
+		"joinWaitlist": "Sluit je aan bij de wachtlijst voor vroege toegang."
+	},
 	"editMessage": {
 		"placeholder": "Bewerk je bericht..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/pl/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Wersja {{version}} - Kliknij, aby wyświetlić informacje o wydaniu"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud już wkrótce!",
+		"description": "Uruchamiaj zdalne agenty w chmurze, uzyskuj dostęp do swoich zadań z dowolnego miejsca, współpracuj z innymi i wiele więcej.",
+		"joinWaitlist": "Dołącz do listy oczekujących, aby uzyskać wczesny dostęp."
+	},
 	"editMessage": {
 		"placeholder": "Edytuj swoją wiadomość..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/pt-BR/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Versão {{version}} - Clique para ver as notas de lançamento"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud chegará em breve!",
+		"description": "Execute agentes remotos na nuvem, acesse suas tarefas de qualquer lugar, colabore com outros e muito mais.",
+		"joinWaitlist": "Junte-se à lista de espera para obter acesso antecipado."
+	},
 	"editMessage": {
 		"placeholder": "Edite sua mensagem..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/ru/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Версия {{version}} - Нажмите, чтобы просмотреть примечания к выпуску"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud скоро появится!",
+		"description": "Запускайте удаленные агенты в облаке, получайте доступ к своим задачам из любого места, сотрудничайте с другими и многое другое.",
+		"joinWaitlist": "Присоединитесь к списку ожидания для получения раннего доступа."
+	},
 	"editMessage": {
 		"placeholder": "Редактировать сообщение..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/tr/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Sürüm {{version}} - Sürüm notlarını görüntülemek için tıklayın"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud yakında geliyor!",
+		"description": "Bulutta uzak ajanlar çalıştırın, görevlerinize her yerden erişin, başkalarıyla işbirliği yapın ve daha fazlası.",
+		"joinWaitlist": "Erken erişim için bekleme listesine katılın."
+	},
 	"editMessage": {
 		"placeholder": "Mesajını düzenle..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/vi/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "Phiên bản {{version}} - Nhấp để xem ghi chú phát hành"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud sắp ra mắt!",
+		"description": "Chạy các agent từ xa trên cloud, truy cập các tác vụ của bạn từ mọi nơi, cộng tác với người khác và nhiều hơn nữa.",
+		"joinWaitlist": "Tham gia danh sách chờ để được truy cập sớm."
+	},
 	"editMessage": {
 		"placeholder": "Chỉnh sửa tin nhắn của bạn..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/zh-CN/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "版本 {{version}} - 点击查看发布说明"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud 即将推出!",
+		"description": "在云端运行远程代理,随时随地访问任务,与他人协作等更多功能。",
+		"joinWaitlist": "加入等待列表获取早期访问权限。"
+	},
 	"editMessage": {
 		"placeholder": "编辑消息..."
 	}

+ 5 - 0
webview-ui/src/i18n/locales/zh-TW/chat.json

@@ -323,6 +323,11 @@
 	"versionIndicator": {
 		"ariaLabel": "版本 {{version}} - 點擊查看發布說明"
 	},
+	"rooCloudCTA": {
+		"title": "Roo Code Cloud 即將推出!",
+		"description": "在雲端執行遠端代理,隨時隨地存取您的工作,與他人協作等更多功能。",
+		"joinWaitlist": "加入等候名單以獲得早期存取權限。"
+	},
 	"editMessage": {
 		"placeholder": "編輯訊息..."
 	}