Skip to content

Commit b0f1eb6

Browse files
committed
ExtensionStateContext does not correctly merge state
1 parent a2d441c commit b0f1eb6

File tree

5 files changed

+70
-10
lines changed

5 files changed

+70
-10
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"roo-cline": patch
3+
---
4+
5+
ExtensionStateContext does not correctly merge state

webview-ui/package-lock.json

Lines changed: 9 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"debounce": "^2.1.1",
3535
"fast-deep-equal": "^3.1.3",
3636
"fzf": "^0.5.2",
37+
"lodash": "^4.17.21",
3738
"lucide-react": "^0.475.0",
3839
"mermaid": "^11.4.1",
3940
"react": "^18.3.1",
@@ -61,6 +62,7 @@
6162
"@testing-library/react": "^16.2.0",
6263
"@testing-library/user-event": "^14.6.1",
6364
"@types/jest": "^27.5.2",
65+
"@types/lodash": "^4.17.16",
6466
"@types/node": "^18.0.0",
6567
"@types/react": "^18.3.18",
6668
"@types/react-dom": "^18.3.5",

webview-ui/src/context/ExtensionStateContext.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import React, { createContext, useCallback, useContext, useEffect, useState } from "react"
22
import { useEvent } from "react-use"
3+
import { merge } from "lodash"
4+
35
import { ApiConfigMeta, ExtensionMessage, ExtensionState } from "../../../src/shared/ExtensionMessage"
46
import { ApiConfiguration } from "../../../src/shared/api"
57
import { vscode } from "../utils/vscode"
@@ -123,13 +125,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
123125
switch (message.type) {
124126
case "state": {
125127
const newState = message.state!
126-
setState((prevState) => ({
127-
...prevState,
128-
...newState,
129-
}))
130-
const config = newState.apiConfiguration
131-
const hasKey = checkExistKey(config)
132-
setShowWelcome(!hasKey)
128+
setState((prevState) => mergeExtensionState(prevState, newState))
129+
setShowWelcome(!checkExistKey(newState.apiConfiguration))
133130
setDidHydrateState(true)
134131
break
135132
}
@@ -256,3 +253,6 @@ export const useExtensionState = () => {
256253
}
257254
return context
258255
}
256+
257+
export const mergeExtensionState = (prevState: ExtensionState, newState: ExtensionState): ExtensionState =>
258+
merge(prevState, newState)

webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
1-
import React from "react"
1+
// npx jest webview-ui/src/context/__tests__/ExtensionStateContext.test.tsx
2+
23
import { render, screen, act } from "@testing-library/react"
3-
import { ExtensionStateContextProvider, useExtensionState } from "../ExtensionStateContext"
4+
5+
import { ExtensionState } from "../../../../src/shared/ExtensionMessage"
6+
import { ExtensionStateContextProvider, useExtensionState, mergeExtensionState } from "../ExtensionStateContext"
7+
import { ExperimentId } from "../../../../src/shared/experiments"
8+
import { ApiConfiguration } from "../../../../src/shared/api"
49

510
// Test component that consumes the context
611
const TestComponent = () => {
@@ -63,3 +68,43 @@ describe("ExtensionStateContext", () => {
6368
consoleSpy.mockRestore()
6469
})
6570
})
71+
72+
describe("mergeExtensionState", () => {
73+
it("should correctly merge extension states", () => {
74+
const baseState: ExtensionState = {
75+
version: "",
76+
mcpEnabled: false,
77+
enableMcpServerCreation: false,
78+
clineMessages: [],
79+
taskHistory: [],
80+
shouldShowAnnouncement: false,
81+
enableCheckpoints: true,
82+
preferredLanguage: "English",
83+
writeDelayMs: 1000,
84+
requestDelaySeconds: 5,
85+
rateLimitSeconds: 0,
86+
mode: "default",
87+
experiments: {} as Record<ExperimentId, boolean>,
88+
customModes: [],
89+
maxOpenTabsContext: 20,
90+
apiConfiguration: { providerId: "openrouter" } as ApiConfiguration,
91+
}
92+
93+
const prevState: ExtensionState = {
94+
...baseState,
95+
apiConfiguration: { modelMaxTokens: 1234, modelMaxThinkingTokens: 123 },
96+
}
97+
const newState: ExtensionState = {
98+
...baseState,
99+
apiConfiguration: { modelMaxThinkingTokens: 456, modelTemperature: 0.3 },
100+
}
101+
102+
const result = mergeExtensionState(prevState, newState)
103+
104+
expect(result.apiConfiguration).toEqual({
105+
modelMaxTokens: 1234,
106+
modelMaxThinkingTokens: 456,
107+
modelTemperature: 0.3,
108+
})
109+
})
110+
})

0 commit comments

Comments
 (0)