Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions 5 .changeset/brown-snails-occur.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kilo-code": patch
---

Add subtle fade animation when switching between tabs
3 changes: 3 additions & 0 deletions 3 pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion 3 webview-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
"@roo-code/types": "workspace:^",
"@tailwindcss/vite": "^4.0.0",
"@tanstack/react-query": "^5.68.0",
"@types/qrcode": "^1.5.5",
"@types/seedrandom": "^3.0.8",
"@use-gesture/react": "^10.3.1",
"@types/qrcode": "^1.5.5",
"@vscode/codicons": "^0.0.36",
"@vscode/webview-ui-toolkit": "^1.4.0",
"axios": "^1.12.0",
Expand All @@ -45,6 +45,7 @@
"debounce": "^2.1.1",
"dompurify": "^3.2.6",
"fast-deep-equal": "^3.1.3",
"framer-motion": "12.15.0",
"fzf": "^0.5.2",
"hast-util-to-jsx-runtime": "^2.3.6",
"i18next": "^25.0.0",
Expand Down
6 changes: 4 additions & 2 deletions 6 webview-ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { DeleteMessageDialog, EditMessageDialog } from "./components/chat/Messag
import ErrorBoundary from "./components/ErrorBoundary"
// import { AccountView } from "./components/account/AccountView" // kilocode_change: we have our own profile view
// import { CloudView } from "./components/cloud/CloudView" // kilocode_change: not rendering this
import { AnimatedTabContainer } from "./components/ui/AnimatedTabContainer" // kilocode_change
import { useAddNonInteractiveClickListener } from "./components/ui/hooks/useNonInteractiveClick"
import { TooltipProvider } from "./components/ui/tooltip"
import { STANDARD_TOOLTIP_DELAY } from "./components/ui/standard-tooltip"
Expand Down Expand Up @@ -278,7 +279,8 @@ const App = () => {
return showWelcome ? (
<WelcomeView />
) : (
<>
// kilocode_change- wrap tabs in AnimatedTabContainer
<AnimatedTabContainer tabKey={tab}>
{tab === "modes" && <ModesView onDone={() => switchTab("chat")} />}
{tab === "mcp" && <McpView onDone={() => switchTab("chat")} />}
{tab === "history" && <HistoryView onDone={() => switchTab("chat")} />}
Expand Down Expand Up @@ -389,7 +391,7 @@ const App = () => {
<BottomControls />
</div>
)}
</>
</AnimatedTabContainer>
)
}

Expand Down
5 changes: 5 additions & 0 deletions 5 webview-ui/src/__tests__/App.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ vi.mock("@src/components/ErrorBoundary", () => ({
default: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}))

// Mock AnimatedTabContainer to remove animation delays in tests
vi.mock("@src/components/ui/AnimatedTabContainer", () => ({
AnimatedTabContainer: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}))

// Mock the telemetry client
vi.mock("@src/utils/TelemetryClient", () => ({
telemetryClient: {
Expand Down
42 changes: 42 additions & 0 deletions 42 webview-ui/src/components/ui/AnimatedTabContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React from "react"
import { motion, AnimatePresence } from "framer-motion"

interface AnimatedTabContainerProps {
children: React.ReactNode
tabKey: string
className?: string
}

const fadeVariants = {
initial: {
opacity: 0,
},
animate: {
opacity: 1,
},
exit: {
opacity: 0,
},
}

const transition = {
duration: 0.1,
ease: "easeOut",
}

export const AnimatedTabContainer: React.FC<AnimatedTabContainerProps> = ({ children, tabKey, className = "" }) => {
return (
<AnimatePresence mode="wait" initial={false}>
<motion.div
key={tabKey}
variants={fadeVariants}
initial="initial"
animate="animate"
exit="exit"
transition={transition}
className={className}>
{children}
</motion.div>
</AnimatePresence>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { render, screen } from "@testing-library/react"
import { AnimatedTabContainer } from "../AnimatedTabContainer"

describe("AnimatedTabContainer", () => {
it("should render children correctly", () => {
render(
<AnimatedTabContainer tabKey="test-tab">
<div data-testid="test-content">Test Content</div>
</AnimatedTabContainer>,
)

expect(screen.getByTestId("test-content")).toBeInTheDocument()
expect(screen.getByText("Test Content")).toBeInTheDocument()
})

it("should apply custom className", () => {
const { container } = render(
<AnimatedTabContainer tabKey="test-tab" className="custom-class">
<div>Test Content</div>
</AnimatedTabContainer>,
)

const motionDiv = container.querySelector(".custom-class")
expect(motionDiv).toBeInTheDocument()
})
})
Morty Proxy This is a proxified and sanitized view of the page, visit original site.