diff --git a/src/core/webview/__tests__/ClineProvider.test.ts b/src/core/webview/__tests__/ClineProvider.test.ts index 6449cc93be..df4542eec6 100644 --- a/src/core/webview/__tests__/ClineProvider.test.ts +++ b/src/core/webview/__tests__/ClineProvider.test.ts @@ -8,6 +8,7 @@ import { ExtensionMessage, ExtensionState } from "../../../shared/ExtensionMessa import { setSoundEnabled } from "../../../utils/sound" import { defaultModeSlug } from "../../../shared/modes" import { experimentDefault } from "../../../shared/experiments" +import { defaultExtensionState } from "../../../shared/ExtensionMessage" // Mock setup must come before imports jest.mock("../../prompts/sections/custom-instructions") @@ -352,35 +353,10 @@ describe("ClineProvider", () => { await provider.resolveWebviewView(mockWebviewView) const mockState: ExtensionState = { - version: "1.0.0", - preferredLanguage: "English", - clineMessages: [], - taskHistory: [], - shouldShowAnnouncement: false, + ...defaultExtensionState, apiConfiguration: { apiProvider: "openrouter", }, - customInstructions: undefined, - alwaysAllowReadOnly: false, - alwaysAllowWrite: false, - alwaysAllowExecute: false, - alwaysAllowBrowser: false, - alwaysAllowMcp: false, - uriScheme: "vscode", - soundEnabled: false, - diffEnabled: false, - checkpointsEnabled: false, - writeDelayMs: 1000, - browserViewportSize: "900x600", - fuzzyMatchThreshold: 1.0, - mcpEnabled: true, - enableMcpServerCreation: false, - requestDelaySeconds: 5, - rateLimitSeconds: 0, - mode: defaultModeSlug, - customModes: [], - experiments: experimentDefault, - maxOpenTabsContext: 20, } const message: ExtensionMessage = { diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index e87edffed1..ee6c79a4a2 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -6,7 +6,7 @@ import { McpServer } from "./mcp" import { GitCommit } from "../utils/git" import { Mode, CustomModePrompts, ModeConfig } from "./modes" import { CustomSupportPrompts } from "./support-prompt" -import { ExperimentId } from "./experiments" +import { ExperimentId, experimentDefault } from "./experiments" export interface LanguageModelChatSelector { vendor?: string @@ -86,6 +86,11 @@ export interface ApiConfigMeta { } export interface ExtensionState { + glamaModels?: Record + requestyModels?: Record + openRouterModels?: Record + unboundModels?: Record + openAiModels?: string[] version: string clineMessages: ClineMessage[] taskHistory: HistoryItem[] @@ -240,3 +245,42 @@ export interface ClineApiReqInfo { } export type ClineApiReqCancelReason = "streaming_failed" | "user_cancelled" + +export const defaultExtensionState: ExtensionState = { + version: "1.0.0", + clineMessages: [], + taskHistory: [], + shouldShowAnnouncement: false, + allowedCommands: [], + soundEnabled: false, + soundVolume: 0.5, + diffEnabled: false, + checkpointsEnabled: false, + fuzzyMatchThreshold: 1.0, + preferredLanguage: "English", + writeDelayMs: 1000, + browserViewportSize: "900x600", + screenshotQuality: 75, + terminalOutputLineLimit: 500, + mcpEnabled: true, + enableMcpServerCreation: true, + alwaysApproveResubmit: false, + requestDelaySeconds: 5, + rateLimitSeconds: 0, + currentApiConfigName: "default", + listApiConfigMeta: [], + mode: "code", + customModePrompts: {}, + customSupportPrompts: {}, + experiments: experimentDefault, + enhancementApiConfigId: "", + autoApprovalEnabled: false, + customModes: [], + maxOpenTabsContext: 20, + cwd: "", + glamaModels: {}, + requestyModels: {}, + openRouterModels: {}, + unboundModels: {}, + openAiModels: [], +} diff --git a/webview-ui/.storybook/preview-context.tsx b/webview-ui/.storybook/preview-context.tsx index 368e1868ed..848635f75e 100644 --- a/webview-ui/.storybook/preview-context.tsx +++ b/webview-ui/.storybook/preview-context.tsx @@ -1,4 +1,5 @@ import { ExtensionStateContextType, ExtensionStateContext } from "../src/context/ExtensionStateContext" +import { defaultExtensionState } from "../../src/shared/ExtensionMessage" import { McpServer } from "../../src/shared/mcp" import { ApiConfiguration, ModelInfo } from "../../src/shared/api" import { HistoryItem } from "../../src/shared/HistoryItem" // Import HistoryItem @@ -51,79 +52,17 @@ const mockOpenedTabs: Array<{ label: string; isActive: boolean; path?: string }> ] const defaultContext: ExtensionStateContextType = { - // Version and state - version: "1.0.0", + ...defaultExtensionState, + // Override specific properties for testing + theme: {}, didHydrateState: true, showWelcome: false, - - // Messages and history - clineMessages: [], - taskHistory: [mockTaskHistoryItem], - shouldShowAnnouncement: false, - - // API and models apiConfiguration: mockApiConfig, - glamaModels: {}, - requestyModels: {}, - openRouterModels: {}, - unboundModels: {}, - openAiModels: [], - - // MCP mcpServers: [mockMcpServer], - mcpEnabled: true, - enableMcpServerCreation: true, - - // Files and tabs + taskHistory: [mockTaskHistoryItem], filePaths: mockFilePaths, openedTabs: mockOpenedTabs, - currentCheckpoint: undefined, - - // Settings - mode: "code", - preferredLanguage: "English", - requestDelaySeconds: 0, - rateLimitSeconds: 0, - writeDelayMs: 0, - browserViewportSize: "1200x800", - screenshotQuality: 75, - terminalOutputLineLimit: 500, - fuzzyMatchThreshold: 1.0, - maxOpenTabsContext: 20, - - // Features - diffEnabled: false, - checkpointsEnabled: false, - soundEnabled: false, - soundVolume: 0.5, - autoApprovalEnabled: true, - - // Permissions - alwaysAllowBrowser: true, - alwaysAllowExecute: true, - alwaysAllowMcp: true, - alwaysAllowModeSwitch: true, - alwaysAllowReadOnly: true, - alwaysApproveResubmit: true, - alwaysAllowWrite: true, - allowedCommands: [], - - // Other state - customModePrompts: {}, - customSupportPrompts: {}, - experiments: { - experimentalDiffStrategy: false, - search_and_replace: false, - insert_content: false, - powerSteering: false, - }, - customModes: [], - enhancementApiConfigId: undefined, - currentApiConfigName: "default", - listApiConfigMeta: [], - theme: {}, - - // Setters + // Add setter functions setApiConfiguration: () => {}, setCustomInstructions: () => {}, setAlwaysAllowReadOnly: () => {}, @@ -151,7 +90,6 @@ const defaultContext: ExtensionStateContextType = { setRateLimitSeconds: () => {}, setCurrentApiConfigName: () => {}, setListApiConfigMeta: () => {}, - onUpdateApiConfig: () => {}, setMode: () => {}, setCustomModePrompts: () => {}, setCustomSupportPrompts: () => {}, diff --git a/webview-ui/src/components/chat/__tests__/ChatView.auto-approve.test.tsx b/webview-ui/src/components/chat/__tests__/ChatView.auto-approve.test.tsx index f16e045383..fbe786c397 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.auto-approve.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.auto-approve.test.tsx @@ -2,6 +2,7 @@ import React from "react" import { render, waitFor } from "@testing-library/react" import ChatView from "../ChatView" import { ExtensionStateContextProvider } from "../../../context/ExtensionStateContext" +import { defaultExtensionState } from "../../../../../src/shared/ExtensionMessage" import { vscode } from "../../../utils/vscode" // Mock vscode API @@ -71,13 +72,7 @@ const mockPostMessage = (state: any) => { { type: "state", state: { - version: "1.0.0", - clineMessages: [], - taskHistory: [], - shouldShowAnnouncement: false, - allowedCommands: [], - alwaysAllowExecute: false, - autoApprovalEnabled: true, + ...defaultExtensionState, ...state, }, }, diff --git a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx index 91d12a8d21..1f24c97050 100644 --- a/webview-ui/src/components/chat/__tests__/ChatView.test.tsx +++ b/webview-ui/src/components/chat/__tests__/ChatView.test.tsx @@ -2,6 +2,7 @@ import React from "react" import { render, waitFor } from "@testing-library/react" import ChatView from "../ChatView" import { ExtensionStateContextProvider } from "../../../context/ExtensionStateContext" +import { defaultExtensionState } from "../../../../../src/shared/ExtensionMessage" import { vscode } from "../../../utils/vscode" // Define minimal types needed for testing @@ -130,12 +131,7 @@ const mockPostMessage = (state: Partial) => { { type: "state", state: { - version: "1.0.0", - clineMessages: [], - taskHistory: [], - shouldShowAnnouncement: false, - allowedCommands: [], - alwaysAllowExecute: false, + ...defaultExtensionState, ...state, }, }, diff --git a/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx b/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx index 99bc0a87c2..8cbb8e4bd4 100644 --- a/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx +++ b/webview-ui/src/components/settings/__tests__/SettingsView.test.tsx @@ -1,6 +1,7 @@ import { render, screen, fireEvent } from "@testing-library/react" import SettingsView from "../SettingsView" import { ExtensionStateContextProvider } from "../../../context/ExtensionStateContext" +import { defaultExtensionState } from "../../../../../src/shared/ExtensionMessage" import { vscode } from "../../../utils/vscode" // Mock vscode API @@ -82,14 +83,7 @@ const mockPostMessage = (state: any) => { { type: "state", state: { - version: "1.0.0", - clineMessages: [], - taskHistory: [], - shouldShowAnnouncement: false, - allowedCommands: [], - alwaysAllowExecute: false, - soundEnabled: false, - soundVolume: 0.5, + ...defaultExtensionState, ...state, }, }, diff --git a/webview-ui/src/context/ExtensionStateContext.tsx b/webview-ui/src/context/ExtensionStateContext.tsx index ae5c5b9539..9c2b7464b1 100644 --- a/webview-ui/src/context/ExtensionStateContext.tsx +++ b/webview-ui/src/context/ExtensionStateContext.tsx @@ -1,15 +1,20 @@ import React, { createContext, useCallback, useContext, useEffect, useState } from "react" import { useEvent } from "react-use" -import { ApiConfigMeta, ExtensionMessage, ExtensionState } from "../../../src/shared/ExtensionMessage" -import { ApiConfiguration } from "../../../src/shared/api" +import { + ApiConfigMeta, + ExtensionMessage, + ExtensionState, + defaultExtensionState, +} from "../../../src/shared/ExtensionMessage" +import { ApiConfiguration, ModelInfo } from "../../../src/shared/api" import { vscode } from "../utils/vscode" import { convertTextMateToHljs } from "../utils/textMateToHljs" import { findLastIndex } from "../../../src/shared/array" import { McpServer } from "../../../src/shared/mcp" import { checkExistKey } from "../../../src/shared/checkExistApiConfig" -import { Mode, CustomModePrompts, defaultModeSlug, defaultPrompts, ModeConfig } from "../../../src/shared/modes" +import { Mode, CustomModePrompts, ModeConfig } from "../../../src/shared/modes" import { CustomSupportPrompts } from "../../../src/shared/support-prompt" -import { experimentDefault, ExperimentId } from "../../../src/shared/experiments" +import { ExperimentId } from "../../../src/shared/experiments" export interface ExtensionStateContextType extends ExtensionState { didHydrateState: boolean @@ -58,6 +63,11 @@ export interface ExtensionStateContextType extends ExtensionState { setMode: (value: Mode) => void setCustomModePrompts: (value: CustomModePrompts) => void setCustomSupportPrompts: (value: CustomSupportPrompts) => void + glamaModels?: Record + requestyModels?: Record + openRouterModels?: Record + unboundModels?: Record + openAiModels?: string[] enhancementApiConfigId?: string setEnhancementApiConfigId: (value: string) => void setExperimentEnabled: (id: ExperimentId, enabled: boolean) => void @@ -70,39 +80,7 @@ export interface ExtensionStateContextType extends ExtensionState { export const ExtensionStateContext = createContext(undefined) export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const [state, setState] = useState({ - version: "", - clineMessages: [], - taskHistory: [], - shouldShowAnnouncement: false, - allowedCommands: [], - soundEnabled: false, - soundVolume: 0.5, - diffEnabled: false, - checkpointsEnabled: false, - fuzzyMatchThreshold: 1.0, - preferredLanguage: "English", - writeDelayMs: 1000, - browserViewportSize: "900x600", - screenshotQuality: 75, - terminalOutputLineLimit: 500, - mcpEnabled: true, - enableMcpServerCreation: true, - alwaysApproveResubmit: false, - requestDelaySeconds: 5, - rateLimitSeconds: 0, // Minimum time between successive requests (0 = disabled) - currentApiConfigName: "default", - listApiConfigMeta: [], - mode: defaultModeSlug, - customModePrompts: defaultPrompts, - customSupportPrompts: {}, - experiments: experimentDefault, - enhancementApiConfigId: "", - autoApprovalEnabled: false, - customModes: [], - maxOpenTabsContext: 20, - cwd: "", - }) + const [state, setState] = useState(defaultExtensionState) const [didHydrateState, setDidHydrateState] = useState(false) const [showWelcome, setShowWelcome] = useState(false)