From 25364a4f537d23593d6f0f7553ba38379f71fdfc Mon Sep 17 00:00:00 2001 From: Bruno Bergher Date: Wed, 24 Sep 2025 18:39:15 +0100 Subject: [PATCH] Initial restructuring of onboarding content --- .vscode/launch.json | 2 +- webview-ui/src/components/chat/ChatView.tsx | 8 +- .../src/components/common/TelemetryBanner.tsx | 22 ++-- webview-ui/src/components/welcome/RooTips.tsx | 90 +++++++++------- .../welcome/__tests__/RooTips.spec.tsx | 102 +++++++++++++----- webview-ui/src/i18n/locales/en/welcome.json | 4 +- 6 files changed, 146 insertions(+), 82 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 5f023be65b..524033abe5 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,7 +10,7 @@ "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceFolder}/src"], + "args": ["--extensionDevelopmentPath=${workspaceFolder}/src", "--profile-temp"], "sourceMaps": true, "outFiles": ["${workspaceFolder}/src/dist/**/*.js"], "preLaunchTask": "${defaultBuildTask}", diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index d358c68f1c..5e7406b9c7 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -1776,7 +1776,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction - {telemetrySetting === "unset" && } {(showAnnouncement || showAnnouncementModal) && ( { @@ -1832,14 +1831,16 @@ const ChatViewComponent: React.ForwardRefRenderFunction )}
0 ? "mt-0" : ""} px-3.5 min-[370px]:px-10 pt-5 transition-all duration-300`}> + className={` w-full flex flex-col gap-4 items-start m-auto ${isExpanded && tasks.length > 0 ? "mt-0" : ""} px-3.5 min-[370px]:px-10 pt-5 transition-all duration-300`}> {/* Version indicator in top-right corner - only on welcome screen */} setShowAnnouncementModal(true)} className="absolute top-2 right-3 z-10" /> - +
+ +
{cloudIsAuthenticated || taskHistory.length < 4 ? ( @@ -1989,6 +1990,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction + {telemetrySetting === "unset" && } { } return ( -
+
{/* Close button (X) */} -
{t("welcome:telemetry.helpImprove")}
-
- , - }} - /> + +
+
{t("welcome:telemetry.helpImprove")}
+
+ , + }} + /> +
) diff --git a/webview-ui/src/components/welcome/RooTips.tsx b/webview-ui/src/components/welcome/RooTips.tsx index c0d6682d56..3451e8ff6a 100644 --- a/webview-ui/src/components/welcome/RooTips.tsx +++ b/webview-ui/src/components/welcome/RooTips.tsx @@ -1,55 +1,67 @@ import { VSCodeLink } from "@vscode/webview-ui-toolkit/react" -import { useTranslation } from "react-i18next" -import { Trans } from "react-i18next" import { buildDocLink } from "@src/utils/docLinks" +import { Keyboard, ReplaceAll, LucideIcon, CheckCheck, Users2 } from "lucide-react" +import { Button } from "../ui" -const tips = [ +interface TipItem { + icon: LucideIcon + title: string + description: string +} + +const tipItems: TipItem[] = [ { - icon: "codicon-account", - href: buildDocLink("basic-usage/using-modes", "tips"), - titleKey: "rooTips.customizableModes.title", - descriptionKey: "rooTips.customizableModes.description", + icon: Users2, + title: "Powerful role-specific modes", + description: + "Personas like Architect, Code and Ask which stay on task and deliver results. Create your own or get more in the marketplace.", }, { - icon: "codicon-list-tree", - href: buildDocLink("features/boomerang-tasks", "tips"), - titleKey: "rooTips.boomerangTasks.title", - descriptionKey: "rooTips.boomerangTasks.description", + icon: CheckCheck, + title: "Granular auto-approval", + description: "Make Roo as autonomous as you want as you build confidence. Or go YOLO.", + }, + { + icon: Keyboard, + title: "Highly customizable", + description: + "Fine-tune settings for Roo to work for you, like inference context, model properties, slash commands and more.", + }, + { + icon: ReplaceAll, + title: "Model-agnostic", + description: "Bring your own key, no markup or lock-in.", }, ] const RooTips = () => { - const { t } = useTranslation("chat") - return ( -
-

- - the docs - - ), - }} - /> +

+

Welcome to Roo Code!

+ +

Get a whole dev team in your editor:

+
    + {tipItems.map((item, index) => { + const Icon = item.icon + return ( +
  • + +
    + {item.title} + {item.description} +
    +
  • + ) + })} +
+

+ Learn more in the docs

-
- {tips.map((tip) => ( -
- - - - {t(tip.titleKey)} - - : {t(tip.descriptionKey)} - -
- ))} + +
+

To get started:

+
) diff --git a/webview-ui/src/components/welcome/__tests__/RooTips.spec.tsx b/webview-ui/src/components/welcome/__tests__/RooTips.spec.tsx index eb121aaadf..d384271adf 100644 --- a/webview-ui/src/components/welcome/__tests__/RooTips.spec.tsx +++ b/webview-ui/src/components/welcome/__tests__/RooTips.spec.tsx @@ -1,46 +1,92 @@ -import React from "react" -import { render, screen } from "@/utils/test-utils" - +import { render, screen } from "@testing-library/react" +import { describe, it, expect, vi } from "vitest" import RooTips from "../RooTips" +// Mock react-i18next vi.mock("react-i18next", () => ({ useTranslation: () => ({ - t: (key: string) => key, // Simple mock that returns the key + t: (key: string) => key, }), - Trans: ({ - children, - components, - }: { - children?: React.ReactNode - components?: Record - }) => { - // Simple mock that renders children or the first component if no children - return children || (components && Object.values(components)[0]) || null - }, + Trans: ({ children }: { children: React.ReactNode }) => children, })) -vi.mock("@vscode/webview-ui-toolkit/react", () => ({ - VSCodeLink: ({ href, children }: { href: string; children: React.ReactNode }) => {children}, +// Mock lucide-react icons +vi.mock("lucide-react", () => ({ + ReplaceAll: ({ className }: { className?: string }) =>
, + ChefHat: ({ className }: { className?: string }) =>
, + Keyboard: ({ className }: { className?: string }) =>
, + Wifi: ({ className }: { className?: string }) =>
, + Github: () => null, + Router: () => null, })) -describe("RooTips Component", () => { - beforeEach(() => { - vi.useFakeTimers() +describe("RooTips", () => { + it("renders the welcome heading", () => { + render() + expect(screen.getByText("Welcome to Roo Code!")).toBeInTheDocument() }) - afterEach(() => { - vi.runOnlyPendingTimers() - vi.useRealTimers() + it("renders the subtitle", () => { + render() + expect(screen.getByText("Roo is a powerful AI Coding assistant for serious work:")).toBeInTheDocument() }) - describe("when cycle is false (default)", () => { - beforeEach(() => { - render() + it("renders all tip items", () => { + render() + + // Check titles + expect(screen.getByText("Model-agnostic")).toBeInTheDocument() + expect(screen.getByText("Extensible Role-specific Modes")).toBeInTheDocument() + expect(screen.getByText("Highly customizable")).toBeInTheDocument() + expect(screen.getByText("Produce from anywhere")).toBeInTheDocument() + + // Check descriptions + expect(screen.getByText("Bring your own key, no markup or lock-in")).toBeInTheDocument() + expect( + screen.getByText("Focus the LLM of activities like planning, coding, merging conflicts and more"), + ).toBeInTheDocument() + expect(screen.getByText("Tweak the details that matter to make it work for you")).toBeInTheDocument() + expect( + screen.getByText("Follow and control Roo from any device with Roo Code Cloud (optional)"), + ).toBeInTheDocument() + }) + + it("renders all icons", () => { + render() + + expect(screen.getByTestId("replace-all-icon")).toBeInTheDocument() + expect(screen.getByTestId("chef-hat-icon")).toBeInTheDocument() + expect(screen.getByTestId("keyboard-icon")).toBeInTheDocument() + expect(screen.getByTestId("wifi-icon")).toBeInTheDocument() + }) + + it("renders the docs link text", () => { + render() + expect(screen.getByText("Learn more in the Docs")).toBeInTheDocument() + }) + + it("applies correct CSS classes to list items", () => { + const { container } = render() + const listItems = container.querySelectorAll("li") + + expect(listItems).toHaveLength(4) + listItems.forEach((item) => { + expect(item).toHaveClass("flex", "items-start", "gap-2") }) + }) - test("renders only the top two tips", () => { - // Ensure only two tips are present plus the docs link in the Trans component (3 total links) - expect(screen.getAllByRole("link")).toHaveLength(3) + it("applies correct CSS classes to icons", () => { + render() + + const icons = [ + screen.getByTestId("replace-all-icon"), + screen.getByTestId("chef-hat-icon"), + screen.getByTestId("keyboard-icon"), + screen.getByTestId("wifi-icon"), + ] + + icons.forEach((icon) => { + expect(icon).toHaveClass("size-4", "mt-1", "shrink-0") }) }) }) diff --git a/webview-ui/src/i18n/locales/en/welcome.json b/webview-ui/src/i18n/locales/en/welcome.json index b19245fa8e..2968f1d07c 100644 --- a/webview-ui/src/i18n/locales/en/welcome.json +++ b/webview-ui/src/i18n/locales/en/welcome.json @@ -16,8 +16,8 @@ "startRouter": "We recommend using an LLM Router:", "startCustom": "Or you can bring your provider API key:", "telemetry": { - "helpImprove": "Help Improve Roo Code", - "helpImproveMessage": "Roo Code collects error and usage data to help us fix bugs and improve the extension. This telemetry does not collect code, prompts or personal information. You can turn this off in settings." + "helpImprove": "Anonymous usage analytics", + "helpImproveMessage": "We collect usage and error data to help us fix bugs and improve the product. We don't collect any code, prompts or personal information. Opt out in settings." }, "importSettings": "Import Settings" }