Initial restructuring of onboarding content

This commit is contained in:
Bruno Bergher 2025-09-24 18:39:15 +01:00
parent 8dbd8c4b1b
commit 25364a4f53
6 changed files with 146 additions and 82 deletions

2
.vscode/launch.json vendored
View file

@ -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}",

View file

@ -1776,7 +1776,6 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
<div
data-testid="chat-view"
className={isHidden ? "hidden" : "fixed top-0 left-0 right-0 bottom-0 flex flex-col overflow-hidden"}>
{telemetrySetting === "unset" && <TelemetryBanner />}
{(showAnnouncement || showAnnouncementModal) && (
<Announcement
hideAnnouncement={() => {
@ -1832,14 +1831,16 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
</div>
)}
<div
className={` w-full flex flex-col gap-4 m-auto ${isExpanded && tasks.length > 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 */}
<VersionIndicator
onClick={() => setShowAnnouncementModal(true)}
className="absolute top-2 right-3 z-10"
/>
<RooHero />
<div className="ml-6">
<RooHero />
</div>
<div className="mb-2.5">
{cloudIsAuthenticated || taskHistory.length < 4 ? (
@ -1989,6 +1990,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
}
}}
/>
{telemetrySetting === "unset" && <TelemetryBanner />}
<ChatTextArea
ref={textAreaRef}
inputValue={inputValue}

View file

@ -1,6 +1,7 @@
import { memo, useState } from "react"
import { Trans } from "react-i18next"
import { VSCodeLink } from "@vscode/webview-ui-toolkit/react"
import { ChartColumnIncreasing } from "lucide-react"
import type { TelemetrySetting } from "@roo-code/types"
@ -29,7 +30,7 @@ const TelemetryBanner = () => {
}
return (
<div className="relative px-4 py-2.5 pr-10 bg-vscode-banner-background border-b border-vscode-panel-border text-sm leading-normal text-vscode-foreground">
<div className="relative p-4 mb-4 flex gap-2 bg-vscode-button-secondaryBackground/50 border-b border-vscode-panel-border text-sm text-vscode-foreground">
{/* Close button (X) */}
<button
onClick={handleClose}
@ -38,14 +39,17 @@ const TelemetryBanner = () => {
×
</button>
<div className="mb-0.5 font-bold">{t("welcome:telemetry.helpImprove")}</div>
<div>
<Trans
i18nKey="welcome:telemetry.helpImproveMessage"
components={{
settingsLink: <VSCodeLink href="#" onClick={handleOpenSettings} />,
}}
/>
<ChartColumnIncreasing className="inline size-4 mt-0.5 shrink-0" />
<div className="pr-6 cursor-default">
<div className="mb-0.5 font-bold">{t("welcome:telemetry.helpImprove")}</div>
<div>
<Trans
i18nKey="welcome:telemetry.helpImproveMessage"
components={{
settingsLink: <VSCodeLink href="#" onClick={handleOpenSettings} />,
}}
/>
</div>
</div>
</div>
)

View file

@ -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 (
<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>
),
}}
/>
<div className="text-left text-base font-light pl-7 pr-2">
<h1 className="text-vscode-editor-foreground text-xl">Welcome to Roo Code!</h1>
<p className="font-bold">Get a whole dev team in your editor:</p>
<ul className="space-y-3 -ml-7">
{tipItems.map((item, index) => {
const Icon = item.icon
return (
<li key={index} className="flex items-start gap-3">
<Icon className="size-3.5 mt-0.75 shrink-0" />
<div>
<strong className="block font-semibold mr-1">{item.title}</strong>
<span className="text-vscode-descriptionForeground">{item.description}</span>
</div>
</li>
)
})}
</ul>
<p className="text-vscode-descriptionForeground">
Learn more in the <VSCodeLink href={buildDocLink("", "onboarding-home")}>docs</VSCodeLink>
</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]">
<span className={`codicon ${tip.icon}`}></span>
<span>
<VSCodeLink className="forced-color-adjust-none" href={tip.href}>
{t(tip.titleKey)}
</VSCodeLink>
: {t(tip.descriptionKey)}
</span>
</div>
))}
<div className="mt-12 border-t border-vscode-panel-border">
<p>To get started:</p>
<Button>Configure API Provider</Button>
</div>
</div>
)

View file

@ -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<string, React.ReactElement>
}) => {
// 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 }) => <a href={href}>{children}</a>,
// Mock lucide-react icons
vi.mock("lucide-react", () => ({
ReplaceAll: ({ className }: { className?: string }) => <div data-testid="replace-all-icon" className={className} />,
ChefHat: ({ className }: { className?: string }) => <div data-testid="chef-hat-icon" className={className} />,
Keyboard: ({ className }: { className?: string }) => <div data-testid="keyboard-icon" className={className} />,
Wifi: ({ className }: { className?: string }) => <div data-testid="wifi-icon" className={className} />,
Github: () => null,
Router: () => null,
}))
describe("RooTips Component", () => {
beforeEach(() => {
vi.useFakeTimers()
describe("RooTips", () => {
it("renders the welcome heading", () => {
render(<RooTips />)
expect(screen.getByText("Welcome to Roo Code!")).toBeInTheDocument()
})
afterEach(() => {
vi.runOnlyPendingTimers()
vi.useRealTimers()
it("renders the subtitle", () => {
render(<RooTips />)
expect(screen.getByText("Roo is a powerful AI Coding assistant for serious work:")).toBeInTheDocument()
})
describe("when cycle is false (default)", () => {
beforeEach(() => {
render(<RooTips />)
it("renders all tip items", () => {
render(<RooTips />)
// 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(<RooTips />)
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(<RooTips />)
expect(screen.getByText("Learn more in the Docs")).toBeInTheDocument()
})
it("applies correct CSS classes to list items", () => {
const { container } = render(<RooTips />)
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(<RooTips />)
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")
})
})
})

View file

@ -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 <settingsLink>settings</settingsLink>."
"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 <settingsLink>settings</settingsLink>."
},
"importSettings": "Import Settings"
}