diff --git a/packages/types/src/global-settings.ts b/packages/types/src/global-settings.ts index a56a00fc355a..0b9da235a81f 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(), + useKangarooAnimation: 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 91b868796689..c696a61bf308 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -1793,6 +1793,7 @@ export class ClineProvider terminalCompressProgressBar, historyPreviewCollapsed, reasoningBlockCollapsed, + useKangarooAnimation, cloudUserInfo, cloudIsAuthenticated, sharingEnabled, @@ -1927,6 +1928,7 @@ export class ClineProvider hasSystemPromptOverride, historyPreviewCollapsed: historyPreviewCollapsed ?? false, reasoningBlockCollapsed: reasoningBlockCollapsed ?? true, + useKangarooAnimation: useKangarooAnimation ?? 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, + useKangarooAnimation: stateValues.useKangarooAnimation ?? false, cloudUserInfo, cloudIsAuthenticated, sharingEnabled, diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index af5f9925c353..ae78befd622a 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 "useKangarooAnimation": + await updateGlobalState("useKangarooAnimation", 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 66f389f81c10..199607794d3c 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -288,6 +288,7 @@ export type ExtensionState = Pick< | "openRouterImageGenerationSelectedModel" | "includeTaskHistoryInEnhance" | "reasoningBlockCollapsed" + | "useKangarooAnimation" > & { version: string clineMessages: ClineMessage[] diff --git a/src/shared/WebviewMessage.ts b/src/shared/WebviewMessage.ts index d43a2fce0434..446d60b54254 100644 --- a/src/shared/WebviewMessage.ts +++ b/src/shared/WebviewMessage.ts @@ -195,6 +195,7 @@ export interface WebviewMessage { | "profileThresholds" | "setHistoryPreviewCollapsed" | "setReasoningBlockCollapsed" + | "useKangarooAnimation" | "openExternal" | "filterMarketplaceItems" | "marketplaceButtonClicked" diff --git a/webview-ui/src/components/chat/KangarooLoader.tsx b/webview-ui/src/components/chat/KangarooLoader.tsx new file mode 100644 index 000000000000..41f8703cce07 --- /dev/null +++ b/webview-ui/src/components/chat/KangarooLoader.tsx @@ -0,0 +1,70 @@ +import React from "react" + +interface KangarooLoaderProps { + className?: string + style?: React.CSSProperties +} + +/** + * KangarooLoader - Animated kangaroo icon for progress indication + * + * The kangaroo icon is an inline SVG (embedded directly in the component). + * It uses a vector path definition to create the kangaroo silhouette. + * + * Size is fixed to match VSCodeProgressRing (16px) for consistent UI. + * Animation duration is 0.7s for a smooth, quick bounce effect. + */ +export const KangarooLoader: React.FC = ({ className = "", style }) => { + return ( +
+
+ + + +
+ +
+ ) +} diff --git a/webview-ui/src/components/chat/ProgressIndicator.tsx b/webview-ui/src/components/chat/ProgressIndicator.tsx index da35c196e1be..a04ba31287c6 100644 --- a/webview-ui/src/components/chat/ProgressIndicator.tsx +++ b/webview-ui/src/components/chat/ProgressIndicator.tsx @@ -1,16 +1,37 @@ import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react" +import { useExtensionState } from "@src/context/ExtensionStateContext" +import { KangarooLoader } from "./KangarooLoader" -export const ProgressIndicator = () => ( -
-
- +export const ProgressIndicator = () => { + const { useKangarooAnimation } = useExtensionState() + + if (useKangarooAnimation) { + return ( +
+ +
+ ) + } + + return ( +
+
+ +
-
-) + ) +} diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 93b1b39e506f..165d7f509e03 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -195,6 +195,7 @@ const SettingsView = forwardRef(({ onDone, t openRouterImageApiKey, openRouterImageGenerationSelectedModel, reasoningBlockCollapsed, + useKangarooAnimation, } = cachedState const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration]) @@ -384,6 +385,7 @@ const SettingsView = forwardRef(({ onDone, t vscode.postMessage({ type: "updateSupportPrompt", values: customSupportPrompts || {} }) vscode.postMessage({ type: "includeTaskHistoryInEnhance", bool: includeTaskHistoryInEnhance ?? true }) vscode.postMessage({ type: "setReasoningBlockCollapsed", bool: reasoningBlockCollapsed ?? true }) + vscode.postMessage({ type: "useKangarooAnimation", bool: useKangarooAnimation ?? false }) vscode.postMessage({ type: "upsertApiConfiguration", text: currentApiConfigName, apiConfiguration }) vscode.postMessage({ type: "telemetrySetting", text: telemetrySetting }) vscode.postMessage({ type: "profileThresholds", values: profileThresholds }) @@ -782,6 +784,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 2de16e688224..a68e718e8348 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 + useKangarooAnimation?: boolean setCachedStateField: SetCachedStateField } -export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...props }: UISettingsProps) => { +export const UISettings = ({ + reasoningBlockCollapsed, + useKangarooAnimation = false, + setCachedStateField, + ...props +}: UISettingsProps) => { const { t } = useAppTranslation() const handleReasoningBlockCollapsedChange = (value: boolean) => { @@ -26,6 +32,15 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr }) } + const handleKangarooAnimationChange = (value: boolean) => { + setCachedStateField("useKangarooAnimation", value) + + // Track telemetry event + telemetryClient.capture("ui_settings_kangaroo_animation_changed", { + enabled: value, + }) + } + return (
@@ -49,6 +64,19 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr {t("settings:ui.collapseThinking.description")}
+ + {/* Kangaroo Animation Setting */} +
+ handleKangarooAnimationChange(e.target.checked)} + data-testid="kangaroo-animation-checkbox"> + {t("settings:ui.kangarooAnimation.label")} + +
+ {t("settings:ui.kangarooAnimation.description")} +
+
diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index 542b2385c026..04df40d0e6aa 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -158,6 +158,8 @@ export interface ExtensionStateContextType extends ExtensionState { setMaxDiagnosticMessages: (value: number) => void includeTaskHistoryInEnhance?: boolean setIncludeTaskHistoryInEnhance: (value: boolean) => void + useKangarooAnimation: boolean + setUseKangarooAnimation: (value: boolean) => void } export const ExtensionStateContext = createContext(undefined) @@ -285,6 +287,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode global: {}, }) const [includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance] = useState(true) + const [useKangarooAnimation, setUseKangarooAnimation] = useState(false) // Default to false (use spinner) 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 useKangarooAnimation if present in state message + if ((newState as any).useKangarooAnimation !== undefined) { + setUseKangarooAnimation((newState as any).useKangarooAnimation) + } // Handle marketplace data if present in state message if (newState.marketplaceItems !== undefined) { setMarketplaceItems(newState.marketplaceItems) @@ -559,6 +566,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }, includeTaskHistoryInEnhance, setIncludeTaskHistoryInEnhance, + useKangarooAnimation, + setUseKangarooAnimation, } return {children} diff --git a/webview-ui/src/i18n/locales/ca/settings.json b/webview-ui/src/i18n/locales/ca/settings.json index 611159069b29..d8a9c5dd3457 100644 --- a/webview-ui/src/i18n/locales/ca/settings.json +++ b/webview-ui/src/i18n/locales/ca/settings.json @@ -886,6 +886,10 @@ "collapseThinking": { "label": "Replega els missatges de pensament per defecte", "description": "Quan estigui activat, els blocs de pensament es replegaran per defecte fins que interactuïs amb ells" + }, + "kangarooAnimation": { + "label": "Utilitza l'animació de Roo que salta", + "description": "Substitueix el spinner per defecte per una animació de Roo que salta per als indicadors de progrés" } } } diff --git a/webview-ui/src/i18n/locales/de/settings.json b/webview-ui/src/i18n/locales/de/settings.json index 00827751b0fd..a64a878ff390 100644 --- a/webview-ui/src/i18n/locales/de/settings.json +++ b/webview-ui/src/i18n/locales/de/settings.json @@ -886,6 +886,10 @@ "collapseThinking": { "label": "Gedankenblöcke standardmäßig ausblenden", "description": "Wenn aktiviert, werden Gedankenblöcke standardmäßig ausgeblendet, bis du mit ihnen interagierst" + }, + "kangarooAnimation": { + "label": "Springende Roo-Animation verwenden", + "description": "Den Standardspinner durch eine springende Roo-Animation für Fortschrittsanzeigen ersetzen" } } } diff --git a/webview-ui/src/i18n/locales/en/settings.json b/webview-ui/src/i18n/locales/en/settings.json index dfccc49cc4ce..1d0617cc01da 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" + }, + "kangarooAnimation": { + "label": "Use jumping Roo animation", + "description": "Replace the default spinner with a jumping Roo animation for progress indicators" } }, "prompts": { diff --git a/webview-ui/src/i18n/locales/es/settings.json b/webview-ui/src/i18n/locales/es/settings.json index c1271df82740..f971d1537e24 100644 --- a/webview-ui/src/i18n/locales/es/settings.json +++ b/webview-ui/src/i18n/locales/es/settings.json @@ -886,6 +886,10 @@ "collapseThinking": { "label": "Colapsar mensajes de pensamiento por defecto", "description": "Cuando está activado, los bloques de pensamiento se colapsarán por defecto hasta que interactúes con ellos" + }, + "kangarooAnimation": { + "label": "Usar animación de Roo saltando", + "description": "Sustituye el indicador de carga predeterminado por una animación de Roo saltando para los indicadores de progreso" } } } diff --git a/webview-ui/src/i18n/locales/fr/settings.json b/webview-ui/src/i18n/locales/fr/settings.json index abcf401d6402..e65f8abc9f68 100644 --- a/webview-ui/src/i18n/locales/fr/settings.json +++ b/webview-ui/src/i18n/locales/fr/settings.json @@ -886,6 +886,10 @@ "collapseThinking": { "label": "Réduire les messages de réflexion par défaut", "description": "Si activé, les blocs de réflexion seront réduits par défaut jusqu'à ce que vous interagissiez avec eux" + }, + "kangarooAnimation": { + "label": "Utiliser l'animation de Roo sautant", + "description": "Remplace le spinner par défaut par une animation de Roo sautant pour les indicateurs de progression" } } } diff --git a/webview-ui/src/i18n/locales/hi/settings.json b/webview-ui/src/i18n/locales/hi/settings.json index 975e35411eea..c61935214a6a 100644 --- a/webview-ui/src/i18n/locales/hi/settings.json +++ b/webview-ui/src/i18n/locales/hi/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "सोच संदेशों को डिफ़ॉल्ट रूप से संक्षिप्त करें", "description": "सक्षम होने पर, सोच ब्लॉक आपके द्वारा उनके साथ इंटरैक्ट करने तक डिफ़ॉल्ट रूप से संक्षिप्त रहेंगे" + }, + "kangarooAnimation": { + "label": "जंपिंग Roo एनीमेशन उपयोग करें", + "description": "प्रगति संकेतकों के लिए डिफ़ॉल्ट स्पिनर को जंपिंग Roo एनीमेशन से बदलें" } } } diff --git a/webview-ui/src/i18n/locales/id/settings.json b/webview-ui/src/i18n/locales/id/settings.json index aa2c1172119a..1b314d580d8c 100644 --- a/webview-ui/src/i18n/locales/id/settings.json +++ b/webview-ui/src/i18n/locales/id/settings.json @@ -916,6 +916,10 @@ "collapseThinking": { "label": "Ciutkan pesan Berpikir secara default", "description": "Jika diaktifkan, blok berpikir akan diciutkan secara default sampai Anda berinteraksi dengannya" + }, + "kangarooAnimation": { + "label": "Gunakan Animasi Roo Melompat", + "description": "Ganti pemintal bawaan dengan animasi Roo yang melompat untuk indikator progres" } } } diff --git a/webview-ui/src/i18n/locales/it/settings.json b/webview-ui/src/i18n/locales/it/settings.json index 6f2e06bb8fd6..9e41861394e7 100644 --- a/webview-ui/src/i18n/locales/it/settings.json +++ b/webview-ui/src/i18n/locales/it/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Comprimi i messaggi di pensiero per impostazione predefinita", "description": "Se abilitato, i blocchi di pensiero verranno compressi per impostazione predefinita finché non interagisci con essi" + }, + "kangarooAnimation": { + "label": "Usa l'animazione di Roo che salta", + "description": "Sostituisci lo spinner predefinito con un'animazione di Roo che salta per gli indicatori di avanzamento" } } } diff --git a/webview-ui/src/i18n/locales/ja/settings.json b/webview-ui/src/i18n/locales/ja/settings.json index cc1ea09317eb..42f268e8d468 100644 --- a/webview-ui/src/i18n/locales/ja/settings.json +++ b/webview-ui/src/i18n/locales/ja/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "デフォルトで思考メッセージを折りたたむ", "description": "有効にすると、操作するまで思考ブロックがデフォルトで折りたたまれます" + }, + "kangarooAnimation": { + "label": "ジャンピング Roo アニメーションを使用", + "description": "進行状況インジケーターの既定のスピナーを、ジャンプする Roo のアニメーションに置き換えます" } } } diff --git a/webview-ui/src/i18n/locales/ko/settings.json b/webview-ui/src/i18n/locales/ko/settings.json index 61539cfc4d9f..c807b8cb609a 100644 --- a/webview-ui/src/i18n/locales/ko/settings.json +++ b/webview-ui/src/i18n/locales/ko/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "기본적으로 생각 메시지 접기", "description": "활성화하면 상호 작용할 때까지 생각 블록이 기본적으로 접힙니다" + }, + "kangarooAnimation": { + "label": "점프하는 Roo 애니메이션 사용", + "description": "진행률 표시에서 기본 스피너를 점프하는 Roo 애니메이션으로 교체합니다" } } } diff --git a/webview-ui/src/i18n/locales/nl/settings.json b/webview-ui/src/i18n/locales/nl/settings.json index 41ee3e5910bb..150d272f1828 100644 --- a/webview-ui/src/i18n/locales/nl/settings.json +++ b/webview-ui/src/i18n/locales/nl/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Denkberichten standaard samenvouwen", "description": "Indien ingeschakeld, worden denkblokken standaard samengevouwen totdat je ermee interageert" + }, + "kangarooAnimation": { + "label": "Springende Roo-animatie gebruiken", + "description": "Vervang de standaardspinner door een springende Roo-animatie voor voortgangsindicatoren" } } } diff --git a/webview-ui/src/i18n/locales/pl/settings.json b/webview-ui/src/i18n/locales/pl/settings.json index 6862d6f7edda..4e275a51a372 100644 --- a/webview-ui/src/i18n/locales/pl/settings.json +++ b/webview-ui/src/i18n/locales/pl/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Domyślnie zwijaj komunikaty o myśleniu", "description": "Gdy włączone, bloki myślenia będą domyślnie zwinięte, dopóki nie wejdziesz z nimi w interakcję" + }, + "kangarooAnimation": { + "label": "Użyj animacji skaczącego Roo", + "description": "Zastąp domyślny wskaźnik ładowania animacją skaczącego Roo dla wskaźników postępu" } } } diff --git a/webview-ui/src/i18n/locales/pt-BR/settings.json b/webview-ui/src/i18n/locales/pt-BR/settings.json index b8184777acf0..1db4c4c0f224 100644 --- a/webview-ui/src/i18n/locales/pt-BR/settings.json +++ b/webview-ui/src/i18n/locales/pt-BR/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Recolher mensagens de pensamento por padrão", "description": "Quando ativado, os blocos de pensamento serão recolhidos por padrão até que você interaja com eles" + }, + "kangarooAnimation": { + "label": "Usar animação do Roo pulando", + "description": "Substitua o spinner padrão por uma animação do Roo pulando nos indicadores de progresso" } } } diff --git a/webview-ui/src/i18n/locales/ru/settings.json b/webview-ui/src/i18n/locales/ru/settings.json index bcbd72089a45..91b92903a9bf 100644 --- a/webview-ui/src/i18n/locales/ru/settings.json +++ b/webview-ui/src/i18n/locales/ru/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Сворачивать сообщения о размышлениях по умолчанию", "description": "Если включено, блоки с размышлениями будут свернуты по умолчанию, пока вы не начнете с ними взаимодействовать" + }, + "kangarooAnimation": { + "label": "Использовать анимацию прыгающего Roo", + "description": "Заменить стандартный спиннер на анимацию прыгающего Roo для индикаторов прогресса" } } } diff --git a/webview-ui/src/i18n/locales/tr/settings.json b/webview-ui/src/i18n/locales/tr/settings.json index 4ac28f47d2f2..3d3a331a05d7 100644 --- a/webview-ui/src/i18n/locales/tr/settings.json +++ b/webview-ui/src/i18n/locales/tr/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Düşünme mesajlarını varsayılan olarak daralt", "description": "Etkinleştirildiğinde, düşünme blokları siz onlarla etkileşime girene kadar varsayılan olarak daraltılır" + }, + "kangarooAnimation": { + "label": "Zıplayan Roo Animasyonunu Kullan", + "description": "İlerleme göstergelerinde varsayılan döndürücüyü zıplayan Roo animasyonuyla değiştirin" } } } diff --git a/webview-ui/src/i18n/locales/vi/settings.json b/webview-ui/src/i18n/locales/vi/settings.json index 4303325d068e..bd9b8d231bdb 100644 --- a/webview-ui/src/i18n/locales/vi/settings.json +++ b/webview-ui/src/i18n/locales/vi/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "Thu gọn tin nhắn Suy nghĩ theo mặc định", "description": "Khi được bật, các khối suy nghĩ sẽ được thu gọn theo mặc định cho đến khi bạn tương tác với chúng" + }, + "kangarooAnimation": { + "label": "Sử dụng hoạt ảnh Roo nhảy", + "description": "Thay thế spinner mặc định bằng hoạt ảnh Roo nhảy cho các chỉ báo tiến trình" } } } diff --git a/webview-ui/src/i18n/locales/zh-CN/settings.json b/webview-ui/src/i18n/locales/zh-CN/settings.json index f574106f4568..87163779834b 100644 --- a/webview-ui/src/i18n/locales/zh-CN/settings.json +++ b/webview-ui/src/i18n/locales/zh-CN/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "默认折叠“思考”消息", "description": "启用后,“思考”块将默认折叠,直到您与其交互" + }, + "kangarooAnimation": { + "label": "使用跳跃的 Roo 动画", + "description": "将默认旋转器替换为跳跃的 Roo 动画,用于进度指示器" } } } diff --git a/webview-ui/src/i18n/locales/zh-TW/settings.json b/webview-ui/src/i18n/locales/zh-TW/settings.json index 67e8c43b60a9..19c33da16688 100644 --- a/webview-ui/src/i18n/locales/zh-TW/settings.json +++ b/webview-ui/src/i18n/locales/zh-TW/settings.json @@ -887,6 +887,10 @@ "collapseThinking": { "label": "預設折疊“思考”訊息", "description": "啟用後,“思考”塊將預設折疊,直到您與其互動" + }, + "kangarooAnimation": { + "label": "使用跳躍的 Roo 動畫", + "description": "將預設的旋轉圖示替換為跳躍的 Roo 動畫,用於進度指示器" } } }