From 88fc49d3f3dd0bbcc39ba9731b81699bb1a746b0 Mon Sep 17 00:00:00 2001 From: Roo Code Date: Tue, 21 Oct 2025 12:32:12 +0000 Subject: [PATCH] feat: add optional jumping roo animation for progress indicators - Created JumpingRoo component with CSS keyframes animation - Added useJumpingRooAnimation setting to global settings - Updated ProgressIndicator to conditionally render based on setting - Added toggle in UI Settings panel - Added translation labels for the new setting - Persisted setting in backend state management References #8747 --- packages/types/src/global-settings.ts | 1 + src/core/webview/ClineProvider.ts | 3 + src/core/webview/webviewMessageHandler.ts | 4 + src/shared/ExtensionMessage.ts | 1 + src/shared/WebviewMessage.ts | 1 + webview-ui/src/components/chat/JumpingRoo.tsx | 101 ++++++++++++++++++ .../src/components/chat/ProgressIndicator.tsx | 36 ++++--- .../src/components/settings/SettingsView.tsx | 1 + .../src/components/settings/UISettings.tsx | 30 +++++- .../src/context/ExtensionStateContext.tsx | 9 ++ webview-ui/src/i18n/locales/en/settings.json | 4 + 11 files changed, 177 insertions(+), 14 deletions(-) create mode 100644 webview-ui/src/components/chat/JumpingRoo.tsx diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index a56a00fc355..a3d40d2d714 100644 --- a/packages/types/src/global-settings.ts +++ b/packages/types/src/global-settings.ts @@ -148,6 +148,7 @@ export const globalSettingsSchema = z.object({ includeTaskHistoryInEnhance: z.boolean().optional(), historyPreviewCollapsed: z.boolean().optional(), reasoningBlockCollapsed: z.boolean().optional(), + useJumpingRooAnimation: z.boolean().optional(), profileThresholds: z.record(z.string(), z.number()).optional(), hasOpenedModeSelector: z.boolean().optional(), lastModeExportPath: z.string().optional(), diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index 91b86879668..cd815ce526c 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1793,6 +1793,7 @@ export class ClineProvider terminalCompressProgressBar, historyPreviewCollapsed, reasoningBlockCollapsed, + useJumpingRooAnimation, cloudUserInfo, cloudIsAuthenticated, sharingEnabled, @@ -1927,6 +1928,7 @@ export class ClineProvider hasSystemPromptOverride, historyPreviewCollapsed: historyPreviewCollapsed ?? false, reasoningBlockCollapsed: reasoningBlockCollapsed ?? true, + useJumpingRooAnimation: useJumpingRooAnimation ?? false, cloudUserInfo, cloudIsAuthenticated: cloudIsAuthenticated ?? false, cloudOrganizations, @@ -2142,6 +2144,7 @@ export class ClineProvider maxConcurrentFileReads: stateValues.maxConcurrentFileReads ?? 5, historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false, reasoningBlockCollapsed: stateValues.reasoningBlockCollapsed ?? true, + useJumpingRooAnimation: stateValues.useJumpingRooAnimation ?? false, cloudUserInfo, cloudIsAuthenticated, sharingEnabled, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index af5f9925c35..84c1e21da65 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -1621,6 +1621,10 @@ export const webviewMessageHandler = async ( await updateGlobalState("reasoningBlockCollapsed", message.bool ?? true) // No need to call postStateToWebview here as the UI already updated optimistically break + case "setUseJumpingRooAnimation": + await updateGlobalState("useJumpingRooAnimation", message.bool ?? false) + // No need to call postStateToWebview here as the UI already updated optimistically + break case "toggleApiConfigPin": if (message.text) { const currentPinned = getGlobalState("pinnedApiConfigs") ?? {} diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 66f389f81c1..380fea8bf6d 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -288,6 +288,7 @@ export type ExtensionState = Pick< | "openRouterImageGenerationSelectedModel" | "includeTaskHistoryInEnhance" | "reasoningBlockCollapsed" + | "useJumpingRooAnimation" > & { version: string clineMessages: ClineMessage[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d43a2fce043..c11e1d40c9c 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -195,6 +195,7 @@ export interface WebviewMessage { | "profileThresholds" | "setHistoryPreviewCollapsed" | "setReasoningBlockCollapsed" + | "setUseJumpingRooAnimation" | "openExternal" | "filterMarketplaceItems" | "marketplaceButtonClicked" diff --git a/webview-ui/src/components/chat/JumpingRoo.tsx b/webview-ui/src/components/chat/JumpingRoo.tsx new file mode 100644 index 00000000000..1de20542b88 --- /dev/null +++ b/webview-ui/src/components/chat/JumpingRoo.tsx @@ -0,0 +1,101 @@ +import React from "react" + +export const JumpingRoo = () => ( +
+ + {/* Kangaroo body */} + + {/* Kangaroo head */} + + {/* Kangaroo ears */} + + + {/* Kangaroo tail */} + + {/* Kangaroo legs */} + + + {/* Kangaroo arms */} + + + {/* Kangaroo pouch */} + + + +
+) diff --git a/webview-ui/src/components/chat/ProgressIndicator.tsx b/webview-ui/src/components/chat/ProgressIndicator.tsx index da35c196e1b..330fb86cb72 100644 --- a/webview-ui/src/components/chat/ProgressIndicator.tsx +++ b/webview-ui/src/components/chat/ProgressIndicator.tsx @@ -1,16 +1,26 @@ import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" +import { useExtensionState } from "@/context/ExtensionStateContext" +import { JumpingRoo } from "./JumpingRoo" -export const ProgressIndicator = () => ( -
-
- +export const ProgressIndicator = () => { + const { useJumpingRooAnimation } = useExtensionState() + + if (useJumpingRooAnimation) { + return + } + + return ( +
+
+ +
-
-) + ) +} diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 93b1b39e506..c3352186abc 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -782,6 +782,7 @@ const SettingsView = forwardRef(({ onDone, t {activeTab === "ui" && ( )} diff --git a/webview-ui/src/components/settings/UISettings.tsx b/webview-ui/src/components/settings/UISettings.tsx index 2de16e68822..e2c5d15d008 100644 --- a/webview-ui/src/components/settings/UISettings.tsx +++ b/webview-ui/src/components/settings/UISettings.tsx @@ -11,10 +11,16 @@ import { ExtensionStateContextType } from "@/context/ExtensionStateContext" interface UISettingsProps extends HTMLAttributes { reasoningBlockCollapsed: boolean + useJumpingRooAnimation?: boolean setCachedStateField: SetCachedStateField } -export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...props }: UISettingsProps) => { +export const UISettings = ({ + reasoningBlockCollapsed, + useJumpingRooAnimation, + setCachedStateField, + ...props +}: UISettingsProps) => { const { t } = useAppTranslation() const handleReasoningBlockCollapsedChange = (value: boolean) => { @@ -26,6 +32,15 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr }) } + const handleJumpingRooAnimationChange = (value: boolean) => { + setCachedStateField("useJumpingRooAnimation", value) + + // Track telemetry event + telemetryClient.capture("ui_settings_jumping_roo_changed", { + enabled: value, + }) + } + return (
@@ -49,6 +64,19 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr {t("settings:ui.collapseThinking.description")}
+ + {/* Jumping Roo Animation Setting */} +
+ handleJumpingRooAnimationChange(e.target.checked)} + data-testid="jumping-roo-animation-checkbox"> + {t("settings:ui.jumpingRooAnimation.label")} + +
+ {t("settings:ui.jumpingRooAnimation.description")} +
+
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 542b2385c02..ec51dcaef7e 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -145,6 +145,8 @@ export interface ExtensionStateContextType extends ExtensionState { setTerminalCompressProgressBar: (value: boolean) => void setHistoryPreviewCollapsed: (value: boolean) => void setReasoningBlockCollapsed: (value: boolean) => void + useJumpingRooAnimation?: boolean + setUseJumpingRooAnimation: (value: boolean) => void autoCondenseContext: boolean setAutoCondenseContext: (value: boolean) => void autoCondenseContextPercent: number @@ -285,6 +287,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode global: {}, }) const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true) + const [useJumpingRooAnimation, setUseJumpingRooAnimation] = useState(false) const setListApiConfigMeta = useCallback( (value: ProviderSettingsEntry[]) => setState((prevState) => ({ ...prevState, listApiConfigMeta: value })), @@ -322,6 +325,10 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode if ((newState as any).includeTaskHistoryInEnhance !== undefined) { setIncludeTaskHistoryInEnhance((newState as any).includeTaskHistoryInEnhance) } + // Update useJumpingRooAnimation if present in state message + if ((newState as any).useJumpingRooAnimation !== undefined) { + setUseJumpingRooAnimation((newState as any).useJumpingRooAnimation) + } // Handle marketplace data if present in state message if (newState.marketplaceItems !== undefined) { setMarketplaceItems(newState.marketplaceItems) @@ -537,6 +544,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode setState((prevState) => ({ ...prevState, historyPreviewCollapsed: value })), setReasoningBlockCollapsed: (value) => setState((prevState) => ({ ...prevState, reasoningBlockCollapsed: value })), + useJumpingRooAnimation, + setUseJumpingRooAnimation, setHasOpenedModeSelector: (value) => setState((prevState) => ({ ...prevState, hasOpenedModeSelector: value })), setAutoCondenseContext: (value) => setState((prevState) => ({ ...prevState, autoCondenseContext: value })), setAutoCondenseContextPercent: (value) => diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index dfccc49cc4c..00af5b5406b 100644 --- a/webview-ui/src/i18n/locales/en/settings.json +++ b/webview-ui/src/i18n/locales/en/settings.json @@ -42,6 +42,10 @@ "collapseThinking": { "label": "Collapse Thinking messages by default", "description": "When enabled, thinking blocks will be collapsed by default until you interact with them" + }, + "jumpingRooAnimation": { + "label": "Use Jumping Roo Animation", + "description": "Replace the standard spinner with a jumping kangaroo animation for progress indicators" } }, "prompts": {