From a64cef71b93f1086290840b8f495529040a01c60 Mon Sep 17 00:00:00 2001 From: NaccOll Date: Wed, 20 Aug 2025 23:40:39 +0800 Subject: [PATCH 1/2] refactor: Use strategy pattern instead of case in webviewMessageHandler --- .../handler/WebviewMessageHandlerRegistry.ts | 84 +++++++++++++++++++ .../auto-approval/allowedMaxCostHandler.ts | 10 +++ .../allowedMaxRequestsHandler.ts | 10 +++ .../alwaysAllowBrowserHandler.ts | 10 +++ .../alwaysAllowExecuteHandler.ts | 10 +++ .../alwaysAllowFollowupQuestionsHandler.ts | 10 +++ .../auto-approval/alwaysAllowMcpHandler.ts | 10 +++ .../alwaysAllowModeSwitchHandler.ts | 10 +++ .../alwaysAllowReadOnlyHandler.ts | 10 +++ ...aysAllowReadOnlyOutsideWorkspaceHandler.ts | 10 +++ .../alwaysAllowSubtasksHandler.ts | 10 +++ .../alwaysAllowUpdateTodoListHandler.ts | 10 +++ .../auto-approval/alwaysAllowWriteHandler.ts | 10 +++ ...alwaysAllowWriteOutsideWorkspaceHandler.ts | 10 +++ .../alwaysAllowWriteProtectedHandler.ts | 10 +++ .../alwaysApproveResubmitHandler.ts | 10 +++ .../webview/handler/auto-approval/index.ts | 34 ++++++++ src/core/webview/handler/index.ts | 2 + .../webview/handler/notification/index.ts | 16 ++++ .../handler/notification/playTtsHandler.ts | 15 ++++ .../notification/soundEnabledHandler.ts | 10 +++ .../notification/soundVolumeHandler.ts | 11 +++ .../handler/notification/stopTtsHandler.ts | 10 +++ .../handler/notification/ttsEnabledHandler.ts | 13 +++ .../handler/notification/ttsSpeedHandler.ts | 13 +++ src/core/webview/handler/types.ts | 33 ++++++++ src/core/webview/webviewMessageHandler.ts | 8 ++ 27 files changed, 399 insertions(+) create mode 100644 src/core/webview/handler/WebviewMessageHandlerRegistry.ts create mode 100644 src/core/webview/handler/auto-approval/allowedMaxCostHandler.ts create mode 100644 src/core/webview/handler/auto-approval/allowedMaxRequestsHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowBrowserHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowExecuteHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowFollowupQuestionsHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowMcpHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowModeSwitchHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowReadOnlyHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowReadOnlyOutsideWorkspaceHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowSubtasksHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowUpdateTodoListHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowWriteHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowWriteOutsideWorkspaceHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysAllowWriteProtectedHandler.ts create mode 100644 src/core/webview/handler/auto-approval/alwaysApproveResubmitHandler.ts create mode 100644 src/core/webview/handler/auto-approval/index.ts create mode 100644 src/core/webview/handler/index.ts create mode 100644 src/core/webview/handler/notification/index.ts create mode 100644 src/core/webview/handler/notification/playTtsHandler.ts create mode 100644 src/core/webview/handler/notification/soundEnabledHandler.ts create mode 100644 src/core/webview/handler/notification/soundVolumeHandler.ts create mode 100644 src/core/webview/handler/notification/stopTtsHandler.ts create mode 100644 src/core/webview/handler/notification/ttsEnabledHandler.ts create mode 100644 src/core/webview/handler/notification/ttsSpeedHandler.ts create mode 100644 src/core/webview/handler/types.ts diff --git a/src/core/webview/handler/WebviewMessageHandlerRegistry.ts b/src/core/webview/handler/WebviewMessageHandlerRegistry.ts new file mode 100644 index 000000000000..7b9deb2101c9 --- /dev/null +++ b/src/core/webview/handler/WebviewMessageHandlerRegistry.ts @@ -0,0 +1,84 @@ +import { WebviewMessage } from "../../../shared/WebviewMessage" +import { registerAutoApprovalHandler } from "./auto-approval" +import { registerNotificationHandler } from "./notification" +import { IWebviewMessageHandler, IWebviewMessageHandlerRegistry } from "./types" + +/** + * Singleton registry for managing webview message handlers + * This class maintains a mapping of message types to their respective handlers + */ +export class WebviewMessageHandlerRegistry implements IWebviewMessageHandlerRegistry { + private static instance: WebviewMessageHandlerRegistry + private handlers: Map = new Map() + + private constructor() { + this.registerAllHandlers() + } + + /** + * Get the singleton instance of the registry + */ + public static getInstance(): WebviewMessageHandlerRegistry { + if (!WebviewMessageHandlerRegistry.instance) { + WebviewMessageHandlerRegistry.instance = new WebviewMessageHandlerRegistry() + } + return WebviewMessageHandlerRegistry.instance + } + + /** + * Register a handler for a specific message type + * @param messageType The message type to handle + * @param handler The handler instance + */ + public registerHandler(messageType: WebviewMessage["type"], handler: IWebviewMessageHandler): void { + if (this.handlers.has(messageType)) { + console.warn(`Handler for message type '${messageType}' is already registered. Overwriting.`) + } + this.handlers.set(messageType, handler) + } + + /** + * Get a handler for a specific message type + * @param messageType The message type + * @returns The handler instance or undefined if not found + */ + public getHandler(messageType: WebviewMessage["type"]): IWebviewMessageHandler | undefined { + return this.handlers.get(messageType) + } + + /** + * Get all registered message types + * @returns Array of registered message types + */ + public getRegisteredTypes(): WebviewMessage["type"][] { + return Array.from(this.handlers.keys()) + } + + /** + * Check if a message type has a registered handler + * @param messageType The message type to check + * @returns True if handler exists, false otherwise + */ + public hasHandler(messageType: WebviewMessage["type"]): boolean { + return this.handlers.has(messageType) + } + + /** + * Register all handlers - this method will be expanded as handlers are created + */ + private registerAllHandlers(): void { + registerAutoApprovalHandler(this) + registerNotificationHandler(this) + } + + /** + * Get registry statistics for debugging + * @returns Object containing registry statistics + */ + public getStats(): { totalHandlers: number; registeredTypes: WebviewMessage["type"][] } { + return { + totalHandlers: this.handlers.size, + registeredTypes: this.getRegisteredTypes(), + } + } +} diff --git a/src/core/webview/handler/auto-approval/allowedMaxCostHandler.ts b/src/core/webview/handler/auto-approval/allowedMaxCostHandler.ts new file mode 100644 index 000000000000..290e9a1b3519 --- /dev/null +++ b/src/core/webview/handler/auto-approval/allowedMaxCostHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AllowedMaxCostHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("allowedMaxCost", message.value) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/allowedMaxRequestsHandler.ts b/src/core/webview/handler/auto-approval/allowedMaxRequestsHandler.ts new file mode 100644 index 000000000000..572bd1ecf114 --- /dev/null +++ b/src/core/webview/handler/auto-approval/allowedMaxRequestsHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AllowedMaxRequestsHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("allowedMaxRequests", message.value) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowBrowserHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowBrowserHandler.ts new file mode 100644 index 000000000000..e6c1ad035ec4 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowBrowserHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowBrowserHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowBrowser", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowExecuteHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowExecuteHandler.ts new file mode 100644 index 000000000000..47a5633fa386 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowExecuteHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowExecuteHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowExecute", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowFollowupQuestionsHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowFollowupQuestionsHandler.ts new file mode 100644 index 000000000000..ae54afa0927d --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowFollowupQuestionsHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowFollowupQuestionsHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowFollowupQuestions", message.bool ?? false) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowMcpHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowMcpHandler.ts new file mode 100644 index 000000000000..90fc7fa577e2 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowMcpHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowMcpHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowMcp", message.bool) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowModeSwitchHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowModeSwitchHandler.ts new file mode 100644 index 000000000000..8c91a159dcee --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowModeSwitchHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowModeSwitchHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowModeSwitch", message.bool) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowReadOnlyHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowReadOnlyHandler.ts new file mode 100644 index 000000000000..5469863684d7 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowReadOnlyHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowReadOnlyHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowReadOnly", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowReadOnlyOutsideWorkspaceHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowReadOnlyOutsideWorkspaceHandler.ts new file mode 100644 index 000000000000..e2c82dbd7675 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowReadOnlyOutsideWorkspaceHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowReadOnlyOutsideWorkspaceHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowReadOnlyOutsideWorkspace", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowSubtasksHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowSubtasksHandler.ts new file mode 100644 index 000000000000..972d3d595844 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowSubtasksHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowSubtasksHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowSubtasks", message.bool) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowUpdateTodoListHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowUpdateTodoListHandler.ts new file mode 100644 index 000000000000..01c728c1c583 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowUpdateTodoListHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowUpdateTodoListHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowUpdateTodoList", message.bool) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowWriteHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowWriteHandler.ts new file mode 100644 index 000000000000..037cc2cedadf --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowWriteHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowWriteHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowWrite", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowWriteOutsideWorkspaceHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowWriteOutsideWorkspaceHandler.ts new file mode 100644 index 000000000000..93370deb50b8 --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowWriteOutsideWorkspaceHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowWriteOutsideWorkspaceHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowWriteOutsideWorkspace", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysAllowWriteProtectedHandler.ts b/src/core/webview/handler/auto-approval/alwaysAllowWriteProtectedHandler.ts new file mode 100644 index 000000000000..5d373ffdcc1a --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysAllowWriteProtectedHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysAllowWriteProtectedHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysAllowWriteProtected", message.bool ?? undefined) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/alwaysApproveResubmitHandler.ts b/src/core/webview/handler/auto-approval/alwaysApproveResubmitHandler.ts new file mode 100644 index 000000000000..c7c20a83f65e --- /dev/null +++ b/src/core/webview/handler/auto-approval/alwaysApproveResubmitHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class AlwaysApproveResubmitHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("alwaysApproveResubmit", message.bool ?? false) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/auto-approval/index.ts b/src/core/webview/handler/auto-approval/index.ts new file mode 100644 index 000000000000..62a29a25899a --- /dev/null +++ b/src/core/webview/handler/auto-approval/index.ts @@ -0,0 +1,34 @@ +import { IWebviewMessageHandlerRegistry } from "../types" +import { AlwaysAllowReadOnlyHandler } from "./alwaysAllowReadOnlyHandler" +import { AlwaysAllowReadOnlyOutsideWorkspaceHandler } from "./alwaysAllowReadOnlyOutsideWorkspaceHandler" +import { AlwaysAllowWriteHandler } from "./alwaysAllowWriteHandler" +import { AlwaysAllowWriteOutsideWorkspaceHandler } from "./alwaysAllowWriteOutsideWorkspaceHandler" +import { AlwaysAllowWriteProtectedHandler } from "./alwaysAllowWriteProtectedHandler" +import { AlwaysAllowExecuteHandler } from "./alwaysAllowExecuteHandler" +import { AlwaysAllowBrowserHandler } from "./alwaysAllowBrowserHandler" +import { AlwaysAllowMcpHandler } from "./alwaysAllowMcpHandler" +import { AlwaysAllowModeSwitchHandler } from "./alwaysAllowModeSwitchHandler" +import { AllowedMaxRequestsHandler } from "./allowedMaxRequestsHandler" +import { AllowedMaxCostHandler } from "./allowedMaxCostHandler" +import { AlwaysAllowSubtasksHandler } from "./alwaysAllowSubtasksHandler" +import { AlwaysAllowUpdateTodoListHandler } from "./alwaysAllowUpdateTodoListHandler" +import { AlwaysApproveResubmitHandler } from "./alwaysApproveResubmitHandler" +import { AlwaysAllowFollowupQuestionsHandler } from "./alwaysAllowFollowupQuestionsHandler" + +export function registerAutoApprovalHandler(register: IWebviewMessageHandlerRegistry) { + register.registerHandler("alwaysAllowReadOnly", new AlwaysAllowReadOnlyHandler()) + register.registerHandler("alwaysAllowReadOnlyOutsideWorkspace", new AlwaysAllowReadOnlyOutsideWorkspaceHandler()) + register.registerHandler("alwaysAllowWrite", new AlwaysAllowWriteHandler()) + register.registerHandler("alwaysAllowWriteOutsideWorkspace", new AlwaysAllowWriteOutsideWorkspaceHandler()) + register.registerHandler("alwaysAllowWriteProtected", new AlwaysAllowWriteProtectedHandler()) + register.registerHandler("alwaysAllowBrowser", new AlwaysAllowBrowserHandler()) + register.registerHandler("alwaysAllowExecute", new AlwaysAllowExecuteHandler()) + register.registerHandler("alwaysAllowMcp", new AlwaysAllowMcpHandler()) + register.registerHandler("alwaysAllowModeSwitch", new AlwaysAllowModeSwitchHandler()) + register.registerHandler("allowedMaxRequests", new AllowedMaxRequestsHandler()) + register.registerHandler("allowedMaxCost", new AllowedMaxCostHandler()) + register.registerHandler("alwaysAllowSubtasks", new AlwaysAllowSubtasksHandler()) + register.registerHandler("alwaysAllowUpdateTodoList", new AlwaysAllowUpdateTodoListHandler()) + register.registerHandler("alwaysApproveResubmit", new AlwaysApproveResubmitHandler()) + register.registerHandler("alwaysAllowFollowupQuestions", new AlwaysAllowFollowupQuestionsHandler()) +} diff --git a/src/core/webview/handler/index.ts b/src/core/webview/handler/index.ts new file mode 100644 index 000000000000..6d7721ded83d --- /dev/null +++ b/src/core/webview/handler/index.ts @@ -0,0 +1,2 @@ +export type { IWebviewMessageHandler, IWebviewMessageHandlerRegistry } from "./types" +export { WebviewMessageHandlerRegistry } from "./WebviewMessageHandlerRegistry" diff --git a/src/core/webview/handler/notification/index.ts b/src/core/webview/handler/notification/index.ts new file mode 100644 index 000000000000..b4dd4355c982 --- /dev/null +++ b/src/core/webview/handler/notification/index.ts @@ -0,0 +1,16 @@ +import { IWebviewMessageHandlerRegistry } from "../types" +import { SoundEnabledHandler } from "./soundEnabledHandler" +import { SoundVolumeHandler } from "./soundVolumeHandler" +import { TtsEnabledHandler } from "./ttsEnabledHandler" +import { TtsSpeedHandler } from "./ttsSpeedHandler" +import { PlayTtsHandler } from "./playTtsHandler" +import { StopTtsHandler } from "./stopTtsHandler" + +export function registerNotificationHandler(register: IWebviewMessageHandlerRegistry) { + register.registerHandler("soundEnabled", new SoundEnabledHandler()) + register.registerHandler("soundVolume", new SoundVolumeHandler()) + register.registerHandler("ttsEnabled", new TtsEnabledHandler()) + register.registerHandler("ttsSpeed", new TtsSpeedHandler()) + register.registerHandler("playTts", new PlayTtsHandler()) + register.registerHandler("stopTts", new StopTtsHandler()) +} diff --git a/src/core/webview/handler/notification/playTtsHandler.ts b/src/core/webview/handler/notification/playTtsHandler.ts new file mode 100644 index 000000000000..5eb923071bc8 --- /dev/null +++ b/src/core/webview/handler/notification/playTtsHandler.ts @@ -0,0 +1,15 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" +import { playTts } from "../../../../utils/tts" + +export class PlayTtsHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + if (message.text) { + playTts(message.text, { + onStart: () => provider.postMessageToWebview({ type: "ttsStart", text: message.text }), + onStop: () => provider.postMessageToWebview({ type: "ttsStop", text: message.text }), + }) + } + } +} diff --git a/src/core/webview/handler/notification/soundEnabledHandler.ts b/src/core/webview/handler/notification/soundEnabledHandler.ts new file mode 100644 index 000000000000..09ec5992ac0e --- /dev/null +++ b/src/core/webview/handler/notification/soundEnabledHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class SoundEnabledHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + await provider.contextProxy.setValue("soundEnabled", message.bool ?? true) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/notification/soundVolumeHandler.ts b/src/core/webview/handler/notification/soundVolumeHandler.ts new file mode 100644 index 000000000000..9078627ef82f --- /dev/null +++ b/src/core/webview/handler/notification/soundVolumeHandler.ts @@ -0,0 +1,11 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" + +export class SoundVolumeHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + const soundVolume = message.value ?? 0.5 + await provider.contextProxy.setValue("soundVolume", soundVolume) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/notification/stopTtsHandler.ts b/src/core/webview/handler/notification/stopTtsHandler.ts new file mode 100644 index 000000000000..a8059a973576 --- /dev/null +++ b/src/core/webview/handler/notification/stopTtsHandler.ts @@ -0,0 +1,10 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" +import { stopTts } from "../../../../utils/tts" + +export class StopTtsHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + stopTts() + } +} diff --git a/src/core/webview/handler/notification/ttsEnabledHandler.ts b/src/core/webview/handler/notification/ttsEnabledHandler.ts new file mode 100644 index 000000000000..2d018a20c25d --- /dev/null +++ b/src/core/webview/handler/notification/ttsEnabledHandler.ts @@ -0,0 +1,13 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" +import { setTtsEnabled } from "../../../../utils/tts" + +export class TtsEnabledHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + const ttsEnabled = message.bool ?? true + await provider.contextProxy.setValue("ttsEnabled", ttsEnabled) + setTtsEnabled(ttsEnabled) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/notification/ttsSpeedHandler.ts b/src/core/webview/handler/notification/ttsSpeedHandler.ts new file mode 100644 index 000000000000..7649ab5c12c9 --- /dev/null +++ b/src/core/webview/handler/notification/ttsSpeedHandler.ts @@ -0,0 +1,13 @@ +import { IWebviewMessageHandler } from "../types" +import { ClineProvider } from "../../ClineProvider" +import { WebviewMessage } from "../../../../shared/WebviewMessage" +import { setTtsSpeed } from "../../../../utils/tts" + +export class TtsSpeedHandler implements IWebviewMessageHandler { + async handle(provider: ClineProvider, message: WebviewMessage): Promise { + const ttsSpeed = message.value ?? 1.0 + await provider.contextProxy.setValue("ttsSpeed", ttsSpeed) + setTtsSpeed(ttsSpeed) + await provider.postStateToWebview() + } +} diff --git a/src/core/webview/handler/types.ts b/src/core/webview/handler/types.ts new file mode 100644 index 000000000000..2dece2ba0bc4 --- /dev/null +++ b/src/core/webview/handler/types.ts @@ -0,0 +1,33 @@ +import { WebviewMessage } from "../../../shared/WebviewMessage" +import { ClineProvider } from "../ClineProvider" +import { MarketplaceManager } from "../../../services/marketplace" + +export type WebviewMessageHandlerOptions = { + marketplaceManager?: MarketplaceManager +} + +/** + * Interface for webview message handlers + * Each handler is responsible for processing specific message types + */ +export interface IWebviewMessageHandler { + /** + * Handle a webview message + * @param provider The ClineProvider instance + * @param message The webview message to handle + * @param marketplaceManager Optional marketplace manager + * @returns Promise that resolves when handling is complete + */ + handle(provider: ClineProvider, message: WebviewMessage, options?: WebviewMessageHandlerOptions): Promise +} + +/** + * Interface for the WebviewMessageHandlerRegistry + */ +export interface IWebviewMessageHandlerRegistry { + registerHandler(messageType: WebviewMessage["type"], handler: IWebviewMessageHandler): void + getHandler(messageType: WebviewMessage["type"]): IWebviewMessageHandler | undefined + getRegisteredTypes(): WebviewMessage["type"][] + hasHandler(messageType: WebviewMessage["type"]): boolean + getStats(): { totalHandlers: number; registeredTypes: WebviewMessage["type"][] } +} diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 4dd0fee75ec3..5f8f5e59f81b 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -55,6 +55,7 @@ const ALLOWED_VSCODE_SETTINGS = new Set(["terminal.integrated.inheritEnv"]) import { MarketplaceManager, MarketplaceItemType } from "../../services/marketplace" import { setPendingTodoList } from "../tools/updateTodoListTool" +import { WebviewMessageHandlerRegistry } from "./handler" export const webviewMessageHandler = async ( provider: ClineProvider, @@ -209,6 +210,13 @@ export const webviewMessageHandler = async ( } } + const handlerRegister = WebviewMessageHandlerRegistry.getInstance() + const handler = handlerRegister.getHandler(message.type) + if (handler) { + await handler.handle(provider, message, { marketplaceManager }) + return + } + switch (message.type) { case "webviewDidLaunch": // Load custom modes first From 21777ad83b023908ec7191964be0f34ff9ff5b2a Mon Sep 17 00:00:00 2001 From: NaccOll Date: Thu, 21 Aug 2025 00:23:17 +0800 Subject: [PATCH 2/2] refactor: remove useless message handling cases in webviewMessageHandler --- src/core/webview/webviewMessageHandler.ts | 95 ----------------------- 1 file changed, 95 deletions(-) diff --git a/src/core/webview/webviewMessageHandler.ts b/src/core/webview/webviewMessageHandler.ts index 5f8f5e59f81b..b9326f2bf601 100644 --- a/src/core/webview/webviewMessageHandler.ts +++ b/src/core/webview/webviewMessageHandler.ts @@ -301,58 +301,6 @@ export const webviewMessageHandler = async ( case "customInstructions": await provider.updateCustomInstructions(message.text) break - case "alwaysAllowReadOnly": - await updateGlobalState("alwaysAllowReadOnly", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowReadOnlyOutsideWorkspace": - await updateGlobalState("alwaysAllowReadOnlyOutsideWorkspace", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowWrite": - await updateGlobalState("alwaysAllowWrite", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowWriteOutsideWorkspace": - await updateGlobalState("alwaysAllowWriteOutsideWorkspace", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowWriteProtected": - await updateGlobalState("alwaysAllowWriteProtected", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowExecute": - await updateGlobalState("alwaysAllowExecute", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowBrowser": - await updateGlobalState("alwaysAllowBrowser", message.bool ?? undefined) - await provider.postStateToWebview() - break - case "alwaysAllowMcp": - await updateGlobalState("alwaysAllowMcp", message.bool) - await provider.postStateToWebview() - break - case "alwaysAllowModeSwitch": - await updateGlobalState("alwaysAllowModeSwitch", message.bool) - await provider.postStateToWebview() - break - case "allowedMaxRequests": - await updateGlobalState("allowedMaxRequests", message.value) - await provider.postStateToWebview() - break - case "allowedMaxCost": - await updateGlobalState("allowedMaxCost", message.value) - await provider.postStateToWebview() - break - case "alwaysAllowSubtasks": - await updateGlobalState("alwaysAllowSubtasks", message.bool) - await provider.postStateToWebview() - break - case "alwaysAllowUpdateTodoList": - await updateGlobalState("alwaysAllowUpdateTodoList", message.bool) - await provider.postStateToWebview() - break case "askResponse": provider.getCurrentTask()?.handleWebviewAskResponse(message.askResponse!, message.text, message.images) break @@ -948,40 +896,6 @@ export const webviewMessageHandler = async ( } break } - // playSound handler removed - now handled directly in the webview - case "soundEnabled": - const soundEnabled = message.bool ?? true - await updateGlobalState("soundEnabled", soundEnabled) - await provider.postStateToWebview() - break - case "soundVolume": - const soundVolume = message.value ?? 0.5 - await updateGlobalState("soundVolume", soundVolume) - await provider.postStateToWebview() - break - case "ttsEnabled": - const ttsEnabled = message.bool ?? true - await updateGlobalState("ttsEnabled", ttsEnabled) - setTtsEnabled(ttsEnabled) // Add this line to update the tts utility - await provider.postStateToWebview() - break - case "ttsSpeed": - const ttsSpeed = message.value ?? 1.0 - await updateGlobalState("ttsSpeed", ttsSpeed) - setTtsSpeed(ttsSpeed) - await provider.postStateToWebview() - break - case "playTts": - if (message.text) { - playTts(message.text, { - onStart: () => provider.postMessageToWebview({ type: "ttsStart", text: message.text }), - onStop: () => provider.postMessageToWebview({ type: "ttsStop", text: message.text }), - }) - } - break - case "stopTts": - stopTts() - break case "diffEnabled": const diffEnabled = message.bool ?? true await updateGlobalState("diffEnabled", diffEnabled) @@ -1085,11 +999,6 @@ export const webviewMessageHandler = async ( }) } } - - break - case "alwaysApproveResubmit": - await updateGlobalState("alwaysApproveResubmit", message.bool ?? false) - await provider.postStateToWebview() break case "requestDelaySeconds": await updateGlobalState("requestDelaySeconds", message.value ?? 5) @@ -1275,10 +1184,6 @@ export const webviewMessageHandler = async ( await updateGlobalState("maxWorkspaceFiles", fileCount) await provider.postStateToWebview() break - case "alwaysAllowFollowupQuestions": - await updateGlobalState("alwaysAllowFollowupQuestions", message.bool ?? false) - await provider.postStateToWebview() - break case "followupAutoApproveTimeoutMs": await updateGlobalState("followupAutoApproveTimeoutMs", message.value) await provider.postStateToWebview()