Skip to content
Closed
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
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down
3 changes: 3 additions & 0 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,7 @@ export class ClineProvider
terminalCompressProgressBar,
historyPreviewCollapsed,
reasoningBlockCollapsed,
useJumpingRooAnimation,
cloudUserInfo,
cloudIsAuthenticated,
sharingEnabled,
Expand Down Expand Up @@ -1927,6 +1928,7 @@ export class ClineProvider
hasSystemPromptOverride,
historyPreviewCollapsed: historyPreviewCollapsed ?? false,
reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
useJumpingRooAnimation: useJumpingRooAnimation ?? false,
cloudUserInfo,
cloudIsAuthenticated: cloudIsAuthenticated ?? false,
cloudOrganizations,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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") ?? {}
Expand Down
1 change: 1 addition & 0 deletions src/shared/ExtensionMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ export type ExtensionState = Pick<
| "openRouterImageGenerationSelectedModel"
| "includeTaskHistoryInEnhance"
| "reasoningBlockCollapsed"
| "useJumpingRooAnimation"
> & {
version: string
clineMessages: ClineMessage[]
Expand Down
1 change: 1 addition & 0 deletions src/shared/WebviewMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export interface WebviewMessage {
| "profileThresholds"
| "setHistoryPreviewCollapsed"
| "setReasoningBlockCollapsed"
| "setUseJumpingRooAnimation"
| "openExternal"
| "filterMarketplaceItems"
| "marketplaceButtonClicked"
Expand Down
101 changes: 101 additions & 0 deletions webview-ui/src/components/chat/JumpingRoo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React from "react"

export const JumpingRoo = () => (
<div
style={{
width: "16px",
height: "16px",
display: "flex",
alignItems: "center",
justifyContent: "center",
position: "relative",
}}>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{
animation: "jumpingRoo 0.8s ease-in-out infinite",
}}>
{/* Kangaroo body */}
<ellipse cx="8" cy="10" rx="3.5" ry="4" fill="currentColor" opacity="0.9" />
{/* Kangaroo head */}
<circle cx="8" cy="5" r="2.5" fill="currentColor" opacity="0.9" />
{/* Kangaroo ears */}
<ellipse
cx="6.5"
cy="3.5"
rx="0.8"
ry="1.5"
fill="currentColor"
opacity="0.9"
transform="rotate(-15 6.5 3.5)"
/>
<ellipse
cx="9.5"
cy="3.5"
rx="0.8"
ry="1.5"
fill="currentColor"
opacity="0.9"
transform="rotate(15 9.5 3.5)"
/>
{/* Kangaroo tail */}
<path
d="M5 11 Q2 12 1 14"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
fill="none"
opacity="0.9"
/>
{/* Kangaroo legs */}
<rect x="6" y="12" width="1.5" height="3" rx="0.5" fill="currentColor" opacity="0.9" />
<rect x="8.5" y="12" width="1.5" height="3" rx="0.5" fill="currentColor" opacity="0.9" />
{/* Kangaroo arms */}
<rect
x="5"
y="8"
width="1"
height="2.5"
rx="0.5"
fill="currentColor"
opacity="0.9"
transform="rotate(-20 5.5 9.25)"
/>
<rect
x="10"
y="8"
width="1"
height="2.5"
rx="0.5"
fill="currentColor"
opacity="0.9"
transform="rotate(20 10.5 9.25)"
/>
{/* Kangaroo pouch */}
<path d="M6.5 11 Q8 12 9.5 11" stroke="currentColor" strokeWidth="0.5" fill="none" opacity="0.5" />
</svg>
<style>{`
@keyframes jumpingRoo {
0%, 100% {
transform: translateY(0) scaleY(1);
}
20% {
transform: translateY(0) scaleY(0.9);
}
40% {
transform: translateY(-4px) scaleY(1.1);
}
60% {
transform: translateY(-4px) scaleY(1.1);
}
80% {
transform: translateY(0) scaleY(0.95);
}
}
`}</style>
</div>
)
36 changes: 23 additions & 13 deletions webview-ui/src/components/chat/ProgressIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { VSCodeProgressRing } from "@vscode/webview-ui-toolkit/react"
import { useExtensionState } from "@/context/ExtensionStateContext"
import { JumpingRoo } from "./JumpingRoo"

export const ProgressIndicator = () => (
<div
style={{
width: "16px",
height: "16px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}>
<div style={{ transform: "scale(0.55)", transformOrigin: "center" }}>
<VSCodeProgressRing />
export const ProgressIndicator = () => {
const { useJumpingRooAnimation } = useExtensionState()

if (useJumpingRooAnimation) {
return <JumpingRoo />
}

return (
<div
style={{
width: "16px",
height: "16px",
display: "flex",
alignItems: "center",
justifyContent: "center",
}}>
<div style={{ transform: "scale(0.55)", transformOrigin: "center" }}>
<VSCodeProgressRing />
</div>
</div>
</div>
)
)
}
1 change: 1 addition & 0 deletions webview-ui/src/components/settings/SettingsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
{activeTab === "ui" && (
<UISettings
reasoningBlockCollapsed={reasoningBlockCollapsed ?? true}
useJumpingRooAnimation={extensionState.useJumpingRooAnimation}
setCachedStateField={setCachedStateField}
/>
)}
Expand Down
30 changes: 29 additions & 1 deletion webview-ui/src/components/settings/UISettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,16 @@ import { ExtensionStateContextType } from "@/context/ExtensionStateContext"

interface UISettingsProps extends HTMLAttributes<HTMLDivElement> {
reasoningBlockCollapsed: boolean
useJumpingRooAnimation?: boolean
setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
}

export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...props }: UISettingsProps) => {
export const UISettings = ({
reasoningBlockCollapsed,
useJumpingRooAnimation,
setCachedStateField,
...props
}: UISettingsProps) => {
const { t } = useAppTranslation()

const handleReasoningBlockCollapsedChange = (value: boolean) => {
Expand All @@ -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 (
<div {...props}>
<SectionHeader>
Expand All @@ -49,6 +64,19 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr
{t("settings:ui.collapseThinking.description")}
</div>
</div>

{/* Jumping Roo Animation Setting */}
<div className="flex flex-col gap-1">
<VSCodeCheckbox
checked={useJumpingRooAnimation ?? false}
onChange={(e: any) => handleJumpingRooAnimationChange(e.target.checked)}
data-testid="jumping-roo-animation-checkbox">
<span className="font-medium">{t("settings:ui.jumpingRooAnimation.label")}</span>
</VSCodeCheckbox>
<div className="text-vscode-descriptionForeground text-sm ml-5 mt-1">
{t("settings:ui.jumpingRooAnimation.description")}
</div>
</div>
</div>
</Section>
</div>
Expand Down
9 changes: 9 additions & 0 deletions webview-ui/src/context/ExtensionStateContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 })),
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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) =>
Expand Down
4 changes: 4 additions & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Loading