Просмотр исходного кода

feat: add toggle for Enter key behavior in chat input (#10002)

Hannes Rudolph 2 недель назад
Родитель
Сommit
0cbaed7e7b
45 измененных файлов с 253 добавлено и 14 удалено
  1. 7 0
      packages/types/src/global-settings.ts
  2. 3 0
      src/core/webview/ClineProvider.ts
  3. 1 0
      src/shared/ExtensionMessage.ts
  4. 34 8
      webview-ui/src/components/chat/ChatTextArea.tsx
  5. 66 0
      webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx
  6. 3 0
      webview-ui/src/components/settings/SettingsView.tsx
  7. 39 2
      webview-ui/src/components/settings/UISettings.tsx
  8. 1 0
      webview-ui/src/components/settings/__tests__/UISettings.spec.tsx
  9. 5 0
      webview-ui/src/context/ExtensionStateContext.tsx
  10. 1 0
      webview-ui/src/i18n/locales/ca/chat.json
  11. 4 0
      webview-ui/src/i18n/locales/ca/settings.json
  12. 1 0
      webview-ui/src/i18n/locales/de/chat.json
  13. 4 0
      webview-ui/src/i18n/locales/de/settings.json
  14. 1 0
      webview-ui/src/i18n/locales/en/chat.json
  15. 4 0
      webview-ui/src/i18n/locales/en/settings.json
  16. 1 0
      webview-ui/src/i18n/locales/es/chat.json
  17. 4 0
      webview-ui/src/i18n/locales/es/settings.json
  18. 1 0
      webview-ui/src/i18n/locales/fr/chat.json
  19. 4 0
      webview-ui/src/i18n/locales/fr/settings.json
  20. 1 0
      webview-ui/src/i18n/locales/hi/chat.json
  21. 4 0
      webview-ui/src/i18n/locales/hi/settings.json
  22. 1 0
      webview-ui/src/i18n/locales/id/chat.json
  23. 4 0
      webview-ui/src/i18n/locales/id/settings.json
  24. 1 0
      webview-ui/src/i18n/locales/it/chat.json
  25. 4 0
      webview-ui/src/i18n/locales/it/settings.json
  26. 1 0
      webview-ui/src/i18n/locales/ja/chat.json
  27. 4 0
      webview-ui/src/i18n/locales/ja/settings.json
  28. 1 0
      webview-ui/src/i18n/locales/ko/chat.json
  29. 4 0
      webview-ui/src/i18n/locales/ko/settings.json
  30. 1 0
      webview-ui/src/i18n/locales/nl/chat.json
  31. 4 0
      webview-ui/src/i18n/locales/nl/settings.json
  32. 1 0
      webview-ui/src/i18n/locales/pl/chat.json
  33. 4 0
      webview-ui/src/i18n/locales/pl/settings.json
  34. 1 0
      webview-ui/src/i18n/locales/pt-BR/chat.json
  35. 4 0
      webview-ui/src/i18n/locales/pt-BR/settings.json
  36. 1 0
      webview-ui/src/i18n/locales/ru/chat.json
  37. 4 0
      webview-ui/src/i18n/locales/ru/settings.json
  38. 1 0
      webview-ui/src/i18n/locales/tr/chat.json
  39. 4 0
      webview-ui/src/i18n/locales/tr/settings.json
  40. 1 0
      webview-ui/src/i18n/locales/vi/chat.json
  41. 4 0
      webview-ui/src/i18n/locales/vi/settings.json
  42. 1 0
      webview-ui/src/i18n/locales/zh-CN/chat.json
  43. 6 2
      webview-ui/src/i18n/locales/zh-CN/settings.json
  44. 1 0
      webview-ui/src/i18n/locales/zh-TW/chat.json
  45. 6 2
      webview-ui/src/i18n/locales/zh-TW/settings.json

+ 7 - 0
packages/types/src/global-settings.ts

@@ -187,6 +187,13 @@ export const globalSettingsSchema = z.object({
 	includeTaskHistoryInEnhance: z.boolean().optional(),
 	historyPreviewCollapsed: z.boolean().optional(),
 	reasoningBlockCollapsed: z.boolean().optional(),
+	/**
+	 * Controls the keyboard behavior for sending messages in the chat input.
+	 * - "send": Enter sends message, Shift+Enter creates newline (default)
+	 * - "newline": Enter creates newline, Shift+Enter/Ctrl+Enter sends message
+	 * @default "send"
+	 */
+	enterBehavior: z.enum(["send", "newline"]).optional(),
 	profileThresholds: z.record(z.string(), z.number()).optional(),
 	hasOpenedModeSelector: z.boolean().optional(),
 	lastModeExportPath: z.string().optional(),

+ 3 - 0
src/core/webview/ClineProvider.ts

@@ -1872,6 +1872,7 @@ export class ClineProvider
 			terminalCompressProgressBar,
 			historyPreviewCollapsed,
 			reasoningBlockCollapsed,
+			enterBehavior,
 			cloudUserInfo,
 			cloudIsAuthenticated,
 			sharingEnabled,
@@ -2025,6 +2026,7 @@ export class ClineProvider
 			hasSystemPromptOverride,
 			historyPreviewCollapsed: historyPreviewCollapsed ?? false,
 			reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
+			enterBehavior: enterBehavior ?? "send",
 			cloudUserInfo,
 			cloudIsAuthenticated: cloudIsAuthenticated ?? false,
 			cloudOrganizations,
@@ -2254,6 +2256,7 @@ export class ClineProvider
 			maxConcurrentFileReads: stateValues.maxConcurrentFileReads ?? 5,
 			historyPreviewCollapsed: stateValues.historyPreviewCollapsed ?? false,
 			reasoningBlockCollapsed: stateValues.reasoningBlockCollapsed ?? true,
+			enterBehavior: stateValues.enterBehavior ?? "send",
 			cloudUserInfo,
 			cloudIsAuthenticated,
 			sharingEnabled,

+ 1 - 0
src/shared/ExtensionMessage.ts

@@ -286,6 +286,7 @@ export type ExtensionState = Pick<
 	| "openRouterImageGenerationSelectedModel"
 	| "includeTaskHistoryInEnhance"
 	| "reasoningBlockCollapsed"
+	| "enterBehavior"
 	| "includeCurrentTime"
 	| "includeCurrentCost"
 	| "maxGitStatusFiles"

+ 34 - 8
webview-ui/src/components/chat/ChatTextArea.tsx

@@ -94,6 +94,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
 			clineMessages,
 			commands,
 			cloudUserInfo,
+			enterBehavior,
 		} = useExtensionState()
 
 		// Find the ID and display text for the currently selected API configuration.
@@ -257,6 +258,17 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
 			return inputValue.trim().length > 0 || selectedImages.length > 0
 		}, [inputValue, selectedImages])
 
+		// Compute the key combination text for the send button tooltip based on enterBehavior
+		const sendKeyCombination = useMemo(() => {
+			if (enterBehavior === "newline") {
+				// When Enter = newline, Ctrl/Cmd+Enter sends
+				const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0
+				return isMac ? "⌘+Enter" : "Ctrl+Enter"
+			}
+			// Default: Enter sends
+			return "Enter"
+		}, [enterBehavior])
+
 		const queryItems = useMemo(() => {
 			return [
 				{ type: ContextMenuOptionType.Problems, value: "problems" },
@@ -472,12 +484,24 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
 					return
 				}
 
-				if (event.key === "Enter" && !event.shiftKey && !isComposing) {
-					event.preventDefault()
-
-					// Always call onSend - let ChatView handle queueing when disabled
-					resetHistoryNavigation()
-					onSend()
+				// Handle Enter key based on enterBehavior setting
+				if (event.key === "Enter" && !isComposing) {
+					if (enterBehavior === "newline") {
+						// New behavior: Enter = newline, Shift+Enter or Ctrl+Enter = send
+						if (event.shiftKey || event.ctrlKey || event.metaKey) {
+							event.preventDefault()
+							resetHistoryNavigation()
+							onSend()
+						}
+						// Otherwise, let Enter create newline (don't preventDefault)
+					} else {
+						// Default behavior: Enter = send, Shift+Enter = newline
+						if (!event.shiftKey) {
+							event.preventDefault()
+							resetHistoryNavigation()
+							onSend()
+						}
+					}
 				}
 
 				if (event.key === "Backspace" && !isComposing) {
@@ -541,6 +565,7 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
 				handleHistoryNavigation,
 				resetHistoryNavigation,
 				commands,
+				enterBehavior,
 			],
 		)
 
@@ -1159,9 +1184,10 @@ export const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
 										</button>
 									</StandardTooltip>
 								)}
-								<StandardTooltip content={t("chat:sendMessage")}>
+								<StandardTooltip
+									content={t("chat:pressToSend", { keyCombination: sendKeyCombination })}>
 									<button
-										aria-label={t("chat:sendMessage")}
+										aria-label={t("chat:pressToSend", { keyCombination: sendKeyCombination })}
 										disabled={false}
 										onClick={onSend}
 										className={cn(

+ 66 - 0
webview-ui/src/components/chat/__tests__/ChatTextArea.spec.tsx

@@ -1056,6 +1056,72 @@ describe("ChatTextArea", () => {
 			const apiConfigDropdown = getApiConfigDropdown()
 			expect(apiConfigDropdown).toHaveAttribute("disabled")
 		})
+
+		describe("enter key behavior", () => {
+			it("should send on Enter and allow newline on Shift+Enter in default mode", () => {
+				const onSend = vi.fn()
+
+				;(useExtensionState as ReturnType<typeof vi.fn>).mockReturnValue({
+					filePaths: [],
+					openedTabs: [],
+					taskHistory: [],
+					cwd: "/test/workspace",
+				})
+
+				const { container } = render(<ChatTextArea {...defaultProps} onSend={onSend} />)
+
+				const textarea = container.querySelector("textarea")!
+
+				fireEvent.keyDown(textarea, { key: "Enter" })
+				expect(onSend).toHaveBeenCalledTimes(1)
+
+				const shiftEnterEvent = new KeyboardEvent("keydown", { key: "Enter", shiftKey: true, bubbles: true })
+				fireEvent(textarea, shiftEnterEvent)
+				expect(onSend).toHaveBeenCalledTimes(1)
+				expect(shiftEnterEvent.defaultPrevented).toBe(false)
+			})
+
+			it("should treat Ctrl/Cmd/Shift+Enter as send and plain Enter as newline in newline mode", () => {
+				const onSend = vi.fn()
+
+				;(useExtensionState as ReturnType<typeof vi.fn>).mockReturnValue({
+					filePaths: [],
+					openedTabs: [],
+					taskHistory: [],
+					cwd: "/test/workspace",
+					enterBehavior: "newline",
+				})
+
+				const { container } = render(<ChatTextArea {...defaultProps} onSend={onSend} />)
+
+				const textarea = container.querySelector("textarea")!
+
+				const plainEnterEvent = new KeyboardEvent("keydown", { key: "Enter", bubbles: true, cancelable: true })
+				fireEvent(textarea, plainEnterEvent)
+				expect(onSend).not.toHaveBeenCalled()
+				expect(plainEnterEvent.defaultPrevented).toBe(false)
+
+				const ctrlEnterEvent = new KeyboardEvent("keydown", {
+					key: "Enter",
+					ctrlKey: true,
+					bubbles: true,
+					cancelable: true,
+				})
+				fireEvent(textarea, ctrlEnterEvent)
+				expect(onSend).toHaveBeenCalledTimes(1)
+				expect(ctrlEnterEvent.defaultPrevented).toBe(true)
+
+				const shiftEnterEvent = new KeyboardEvent("keydown", {
+					key: "Enter",
+					shiftKey: true,
+					bubbles: true,
+					cancelable: true,
+				})
+				fireEvent(textarea, shiftEnterEvent)
+				expect(onSend).toHaveBeenCalledTimes(2)
+				expect(shiftEnterEvent.defaultPrevented).toBe(true)
+			})
+		})
 	})
 
 	describe("send button visibility", () => {

+ 3 - 0
webview-ui/src/components/settings/SettingsView.tsx

@@ -210,6 +210,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 		openRouterImageApiKey,
 		openRouterImageGenerationSelectedModel,
 		reasoningBlockCollapsed,
+		enterBehavior,
 		includeCurrentTime,
 		includeCurrentCost,
 		maxGitStatusFiles,
@@ -413,6 +414,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 					condensingApiConfigId: condensingApiConfigId || "",
 					includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true,
 					reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
+					enterBehavior: enterBehavior ?? "send",
 					includeCurrentTime: includeCurrentTime ?? true,
 					includeCurrentCost: includeCurrentCost ?? true,
 					maxGitStatusFiles: maxGitStatusFiles ?? 0,
@@ -833,6 +835,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
 					{activeTab === "ui" && (
 						<UISettings
 							reasoningBlockCollapsed={reasoningBlockCollapsed ?? true}
+							enterBehavior={enterBehavior ?? "send"}
 							setCachedStateField={setCachedStateField}
 						/>
 					)}

+ 39 - 2
webview-ui/src/components/settings/UISettings.tsx

@@ -1,4 +1,4 @@
-import { HTMLAttributes } from "react"
+import { HTMLAttributes, useMemo } from "react"
 import { useAppTranslation } from "@/i18n/TranslationContext"
 import { VSCodeCheckbox } from "@vscode/webview-ui-toolkit/react"
 import { Glasses } from "lucide-react"
@@ -11,12 +11,24 @@ import { ExtensionStateContextType } from "@/context/ExtensionStateContext"
 
 interface UISettingsProps extends HTMLAttributes<HTMLDivElement> {
 	reasoningBlockCollapsed: boolean
+	enterBehavior: "send" | "newline"
 	setCachedStateField: SetCachedStateField<keyof ExtensionStateContextType>
 }
 
-export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...props }: UISettingsProps) => {
+export const UISettings = ({
+	reasoningBlockCollapsed,
+	enterBehavior,
+	setCachedStateField,
+	...props
+}: UISettingsProps) => {
 	const { t } = useAppTranslation()
 
+	// Detect platform for dynamic modifier key display
+	const primaryMod = useMemo(() => {
+		const isMac = navigator.platform.toUpperCase().indexOf("MAC") >= 0
+		return isMac ? "⌘" : "Ctrl"
+	}, [])
+
 	const handleReasoningBlockCollapsedChange = (value: boolean) => {
 		setCachedStateField("reasoningBlockCollapsed", value)
 
@@ -26,6 +38,16 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr
 		})
 	}
 
+	const handleEnterBehaviorChange = (requireCtrlEnter: boolean) => {
+		const newBehavior = requireCtrlEnter ? "newline" : "send"
+		setCachedStateField("enterBehavior", newBehavior)
+
+		// Track telemetry event
+		telemetryClient.capture("ui_settings_enter_behavior_changed", {
+			behavior: newBehavior,
+		})
+	}
+
 	return (
 		<div {...props}>
 			<SectionHeader>
@@ -49,6 +71,21 @@ export const UISettings = ({ reasoningBlockCollapsed, setCachedStateField, ...pr
 							{t("settings:ui.collapseThinking.description")}
 						</div>
 					</div>
+
+					{/* Enter Key Behavior Setting */}
+					<div className="flex flex-col gap-1">
+						<VSCodeCheckbox
+							checked={enterBehavior === "newline"}
+							onChange={(e: any) => handleEnterBehaviorChange(e.target.checked)}
+							data-testid="enter-behavior-checkbox">
+							<span className="font-medium">
+								{t("settings:ui.requireCtrlEnterToSend.label", { primaryMod })}
+							</span>
+						</VSCodeCheckbox>
+						<div className="text-vscode-descriptionForeground text-sm ml-5 mt-1">
+							{t("settings:ui.requireCtrlEnterToSend.description", { primaryMod })}
+						</div>
+					</div>
 				</div>
 			</Section>
 		</div>

+ 1 - 0
webview-ui/src/components/settings/__tests__/UISettings.spec.tsx

@@ -5,6 +5,7 @@ import { UISettings } from "../UISettings"
 describe("UISettings", () => {
 	const defaultProps = {
 		reasoningBlockCollapsed: false,
+		enterBehavior: "send" as const,
 		setCachedStateField: vi.fn(),
 	}
 

+ 5 - 0
webview-ui/src/context/ExtensionStateContext.tsx

@@ -148,6 +148,8 @@ export interface ExtensionStateContextType extends ExtensionState {
 	setTerminalCompressProgressBar: (value: boolean) => void
 	setHistoryPreviewCollapsed: (value: boolean) => void
 	setReasoningBlockCollapsed: (value: boolean) => void
+	enterBehavior?: "send" | "newline"
+	setEnterBehavior: (value: "send" | "newline") => void
 	autoCondenseContext: boolean
 	setAutoCondenseContext: (value: boolean) => void
 	autoCondenseContextPercent: number
@@ -251,6 +253,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		terminalCompressProgressBar: true, // Default to compress progress bar output
 		historyPreviewCollapsed: false, // Initialize the new state (default to expanded)
 		reasoningBlockCollapsed: true, // Default to collapsed
+		enterBehavior: "send", // Default: Enter sends, Shift+Enter creates newline
 		cloudUserInfo: null,
 		cloudIsAuthenticated: false,
 		cloudOrganizations: [],
@@ -571,6 +574,8 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 			setState((prevState) => ({ ...prevState, historyPreviewCollapsed: value })),
 		setReasoningBlockCollapsed: (value) =>
 			setState((prevState) => ({ ...prevState, reasoningBlockCollapsed: value })),
+		enterBehavior: state.enterBehavior ?? "send",
+		setEnterBehavior: (value) => setState((prevState) => ({ ...prevState, enterBehavior: value })),
 		setHasOpenedModeSelector: (value) => setState((prevState) => ({ ...prevState, hasOpenedModeSelector: value })),
 		setAutoCondenseContext: (value) => setState((prevState) => ({ ...prevState, autoCondenseContext: value })),
 		setAutoCondenseContextPercent: (value) =>

+ 1 - 0
webview-ui/src/i18n/locales/ca/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Millora la sol·licitud amb context addicional",
 	"addImages": "Afegeix imatges al missatge",
 	"sendMessage": "Envia el missatge",
+	"pressToSend": "Prem {{keyCombination}} per enviar",
 	"stopTts": "Atura la síntesi de veu",
 	"typeMessage": "Escriu un missatge...",
 	"typeTask": "Escriu la teva tasca aquí...",

+ 4 - 0
webview-ui/src/i18n/locales/ca/settings.json

@@ -954,6 +954,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Requereix {{primaryMod}}+Intro per enviar missatges",
+			"description": "Quan estigui activat, has de prémer {{primaryMod}}+Intro per enviar missatges en lloc de només Intro"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/de/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Prompt mit zusätzlichem Kontext verbessern",
 	"addImages": "Bilder zur Nachricht hinzufügen",
 	"sendMessage": "Nachricht senden",
+	"pressToSend": "Drücke {{keyCombination}} zum Senden",
 	"stopTts": "Text-in-Sprache beenden",
 	"typeMessage": "Nachricht eingeben...",
 	"typeTask": "Gib deine Aufgabe hier ein...",

+ 4 - 0
webview-ui/src/i18n/locales/de/settings.json

@@ -954,6 +954,10 @@
 		"collapseThinking": {
 			"label": "Gedankenblöcke standardmäßig ausblenden",
 			"description": "Wenn aktiviert, werden Gedankenblöcke standardmäßig ausgeblendet, bis du mit ihnen interagierst"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "{{primaryMod}}+Enter zum Senden erfordern",
+			"description": "Wenn aktiviert, musst du {{primaryMod}}+Enter drücken, um Nachrichten zu senden, anstatt nur Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/en/chat.json

@@ -125,6 +125,7 @@
 	"enhancePromptDescription": "The 'Enhance Prompt' button helps improve your prompt by providing additional context, clarification, or rephrasing. Try typing a prompt in here and clicking the button again to see how it works.",
 	"addImages": "Add images to message",
 	"sendMessage": "Send message",
+	"pressToSend": "Press {{keyCombination}} to send",
 	"stopTts": "Stop text-to-speech",
 	"typeMessage": "Type a message...",
 	"typeTask": "Type your task here...",

+ 4 - 0
webview-ui/src/i18n/locales/en/settings.json

@@ -64,6 +64,10 @@
 		"collapseThinking": {
 			"label": "Collapse Thinking messages by default",
 			"description": "When enabled, thinking blocks will be collapsed by default until you interact with them"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Require {{primaryMod}}+Enter to send messages",
+			"description": "When enabled, you must press {{primaryMod}}+Enter to send messages instead of just Enter"
 		}
 	},
 	"prompts": {

+ 1 - 0
webview-ui/src/i18n/locales/es/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Mejorar el mensaje con contexto adicional",
 	"addImages": "Agregar imágenes al mensaje",
 	"sendMessage": "Enviar mensaje",
+	"pressToSend": "Presiona {{keyCombination}} para enviar",
 	"stopTts": "Detener texto a voz",
 	"typeMessage": "Escribe un mensaje...",
 	"typeTask": "Escribe tu tarea aquí...",

+ 4 - 0
webview-ui/src/i18n/locales/es/settings.json

@@ -954,6 +954,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Requerir {{primaryMod}}+Enter para enviar mensajes",
+			"description": "Cuando está activado, debes presionar {{primaryMod}}+Enter para enviar mensajes en lugar de solo Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/fr/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Améliorer la requête avec un contexte supplémentaire",
 	"addImages": "Ajouter des images au message",
 	"sendMessage": "Envoyer le message",
+	"pressToSend": "Appuie sur {{keyCombination}} pour envoyer",
 	"stopTts": "Arrêter la synthèse vocale",
 	"typeMessage": "Écrivez un message...",
 	"typeTask": "Écrivez votre tâche ici...",

+ 4 - 0
webview-ui/src/i18n/locales/fr/settings.json

@@ -954,6 +954,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Exiger {{primaryMod}}+Entrée pour envoyer les messages",
+			"description": "Lorsqu'activé, tu dois appuyer sur {{primaryMod}}+Entrée pour envoyer des messages au lieu de simplement Entrée"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/hi/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "अतिरिक्त संदर्भ के साथ प्रॉम्प्ट बढ़ाएँ",
 	"addImages": "संदेश में चित्र जोड़ें",
 	"sendMessage": "संदेश भेजें",
+	"pressToSend": "भेजने के लिए {{keyCombination}} दबाएं",
 	"stopTts": "टेक्स्ट-टू-स्पीच बंद करें",
 	"typeMessage": "एक संदेश लिखें...",
 	"typeTask": "अपना कार्य यहां लिखें...",

+ 4 - 0
webview-ui/src/i18n/locales/hi/settings.json

@@ -955,6 +955,10 @@
 		"collapseThinking": {
 			"label": "सोच संदेशों को डिफ़ॉल्ट रूप से संक्षिप्त करें",
 			"description": "सक्षम होने पर, सोच ब्लॉक आपके द्वारा उनके साथ इंटरैक्ट करने तक डिफ़ॉल्ट रूप से संक्षिप्त रहेंगे"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "संदेश भेजने के लिए {{primaryMod}}+Enter की आवश्यकता है",
+			"description": "जब सक्षम हो, तो आपको केवल Enter के बजाय संदेश भेजने के लिए {{primaryMod}}+Enter दबाना होगा"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/id/chat.json

@@ -128,6 +128,7 @@
 	},
 	"addImages": "Tambahkan gambar ke pesan",
 	"sendMessage": "Kirim pesan",
+	"pressToSend": "Tekan {{keyCombination}} untuk mengirim",
 	"stopTts": "Hentikan text-to-speech",
 	"typeMessage": "Ketik pesan...",
 	"typeTask": "Bangun, cari, tanya sesuatu",

+ 4 - 0
webview-ui/src/i18n/locales/id/settings.json

@@ -984,6 +984,10 @@
 		"collapseThinking": {
 			"label": "Ciutkan pesan Berpikir secara default",
 			"description": "Jika diaktifkan, blok berpikir akan diciutkan secara default sampai Anda berinteraksi dengannya"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Memerlukan {{primaryMod}}+Enter untuk mengirim pesan",
+			"description": "Ketika diaktifkan, kamu harus menekan {{primaryMod}}+Enter untuk mengirim pesan alih-alih hanya Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/it/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Migliora prompt con contesto aggiuntivo",
 	"addImages": "Aggiungi immagini al messaggio",
 	"sendMessage": "Invia messaggio",
+	"pressToSend": "Premi {{keyCombination}} per inviare",
 	"stopTts": "Interrompi sintesi vocale",
 	"typeMessage": "Scrivi un messaggio...",
 	"typeTask": "Scrivi la tua attività qui...",

+ 4 - 0
webview-ui/src/i18n/locales/it/settings.json

@@ -955,6 +955,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Richiedi {{primaryMod}}+Invio per inviare messaggi",
+			"description": "Quando abilitato, devi premere {{primaryMod}}+Invio per inviare messaggi invece di solo Invio"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/ja/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "追加コンテキストでプロンプトを強化",
 	"addImages": "メッセージに画像を追加",
 	"sendMessage": "メッセージを送信",
+	"pressToSend": "{{keyCombination}}を押して送信",
 	"stopTts": "テキスト読み上げを停止",
 	"typeMessage": "メッセージを入力...",
 	"typeTask": "ここにタスクを入力...",

+ 4 - 0
webview-ui/src/i18n/locales/ja/settings.json

@@ -955,6 +955,10 @@
 		"collapseThinking": {
 			"label": "デフォルトで思考メッセージを折りたたむ",
 			"description": "有効にすると、操作するまで思考ブロックがデフォルトで折りたたまれます"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "メッセージを送信するには{{primaryMod}}+Enterが必要",
+			"description": "有効にすると、Enterだけでなく{{primaryMod}}+Enterを押してメッセージを送信する必要があります"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/ko/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "추가 컨텍스트로 프롬프트 향상",
 	"addImages": "메시지에 이미지 추가",
 	"sendMessage": "메시지 보내기",
+	"pressToSend": "{{keyCombination}}를 눌러 전송",
 	"stopTts": "텍스트 음성 변환 중지",
 	"typeMessage": "메시지 입력...",
 	"typeTask": "여기에 작업 입력...",

+ 4 - 0
webview-ui/src/i18n/locales/ko/settings.json

@@ -955,6 +955,10 @@
 		"collapseThinking": {
 			"label": "기본적으로 생각 메시지 접기",
 			"description": "활성화하면 상호 작용할 때까지 생각 블록이 기본적으로 접힙니다"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "메시지를 보내려면 {{primaryMod}}+Enter가 필요",
+			"description": "활성화하면 Enter만으로는 안 되고 {{primaryMod}}+Enter를 눌러야 메시지를 보낼 수 있습니다"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/nl/chat.json

@@ -114,6 +114,7 @@
 	},
 	"addImages": "Afbeeldingen toevoegen aan bericht",
 	"sendMessage": "Bericht verzenden",
+	"pressToSend": "Druk op {{keyCombination}} om te verzenden",
 	"stopTts": "Stop tekst-naar-spraak",
 	"typeMessage": "Typ een bericht...",
 	"typeTask": "Typ hier je taak...",

+ 4 - 0
webview-ui/src/i18n/locales/nl/settings.json

@@ -955,6 +955,10 @@
 		"collapseThinking": {
 			"label": "Denkberichten standaard samenvouwen",
 			"description": "Indien ingeschakeld, worden denkblokken standaard samengevouwen totdat je ermee interageert"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Vereist {{primaryMod}}+Enter om berichten te versturen",
+			"description": "Wanneer ingeschakeld, moet je {{primaryMod}}+Enter indrukken om berichten te versturen in plaats van alleen Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/pl/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Ulepsz podpowiedź dodatkowym kontekstem",
 	"addImages": "Dodaj obrazy do wiadomości",
 	"sendMessage": "Wyślij wiadomość",
+	"pressToSend": "Naciśnij {{keyCombination}}, aby wysłać",
 	"stopTts": "Zatrzymaj syntezę mowy",
 	"typeMessage": "Wpisz wiadomość...",
 	"typeTask": "Wpisz swoje zadanie tutaj...",

+ 4 - 0
webview-ui/src/i18n/locales/pl/settings.json

@@ -955,6 +955,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ę"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Wymagaj {{primaryMod}}+Enter do wysyłania wiadomości",
+			"description": "Po włączeniu musisz nacisnąć {{primaryMod}}+Enter, aby wysłać wiadomości, zamiast tylko Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/pt-BR/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Aprimorar prompt com contexto adicional",
 	"addImages": "Adicionar imagens à mensagem",
 	"sendMessage": "Enviar mensagem",
+	"pressToSend": "Pressione {{keyCombination}} para enviar",
 	"stopTts": "Parar conversão de texto em fala",
 	"typeMessage": "Digite uma mensagem...",
 	"typeTask": "Digite sua tarefa aqui...",

+ 4 - 0
webview-ui/src/i18n/locales/pt-BR/settings.json

@@ -955,6 +955,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Requer {{primaryMod}}+Enter para enviar mensagens",
+			"description": "Quando ativado, você deve pressionar {{primaryMod}}+Enter para enviar mensagens em vez de apenas Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/ru/chat.json

@@ -114,6 +114,7 @@
 	},
 	"addImages": "Добавить изображения к сообщению",
 	"sendMessage": "Отправить сообщение",
+	"pressToSend": "Нажми {{keyCombination}} для отправки",
 	"stopTts": "Остановить синтез речи",
 	"typeMessage": "Введите сообщение...",
 	"typeTask": "Введите вашу задачу здесь...",

+ 4 - 0
webview-ui/src/i18n/locales/ru/settings.json

@@ -955,6 +955,10 @@
 		"collapseThinking": {
 			"label": "Сворачивать сообщения о размышлениях по умолчанию",
 			"description": "Если включено, блоки с размышлениями будут свернуты по умолчанию, пока вы не начнете с ними взаимодействовать"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Требовать {{primaryMod}}+Enter для отправки сообщений",
+			"description": "Если включено, необходимо нажать {{primaryMod}}+Enter для отправки сообщений вместо простого Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/tr/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Ek bağlamla istemi geliştir",
 	"addImages": "Mesaja resim ekle",
 	"sendMessage": "Mesaj gönder",
+	"pressToSend": "Göndermek için {{keyCombination}} tuşuna bas",
 	"stopTts": "Metin okumayı durdur",
 	"typeMessage": "Bir mesaj yazın...",
 	"typeTask": "Görevinizi buraya yazın...",

+ 4 - 0
webview-ui/src/i18n/locales/tr/settings.json

@@ -955,6 +955,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Mesaj göndermek için {{primaryMod}}+Enter gerekli",
+			"description": "Etkinleştirildiğinde, sadece Enter yerine mesaj göndermek için {{primaryMod}}+Enter'a basmalısınız"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/vi/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "Nâng cao yêu cầu với ngữ cảnh bổ sung",
 	"addImages": "Thêm hình ảnh vào tin nhắn",
 	"sendMessage": "Gửi tin nhắn",
+	"pressToSend": "Nhấn {{keyCombination}} để gửi",
 	"stopTts": "Dừng chuyển văn bản thành giọng nói",
 	"typeMessage": "Nhập tin nhắn...",
 	"typeTask": "Nhập nhiệm vụ của bạn tại đây...",

+ 4 - 0
webview-ui/src/i18n/locales/vi/settings.json

@@ -955,6 +955,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"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "Yêu cầu {{primaryMod}}+Enter để gửi tin nhắn",
+			"description": "Khi được bật, bạn phải nhấn {{primaryMod}}+Enter để gửi tin nhắn thay vì chỉ nhấn Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/zh-CN/chat.json

@@ -105,6 +105,7 @@
 	"enhancePrompt": "增强提示词",
 	"addImages": "添加图片到消息",
 	"sendMessage": "发送消息",
+	"pressToSend": "按 {{keyCombination}} 发送",
 	"stopTts": "停止文本转语音",
 	"typeMessage": "输入消息...",
 	"typeTask": "在此处输入您的任务...",

+ 6 - 2
webview-ui/src/i18n/locales/zh-CN/settings.json

@@ -953,8 +953,12 @@
 	},
 	"ui": {
 		"collapseThinking": {
-			"label": "默认折叠“思考”消息",
-			"description": "启用后,“思考”块将默认折叠,直到您与其交互"
+			"label": "默认折叠「思考」消息",
+			"description": "启用后,「思考」块将默认折叠,直到您与其交互"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "需要 {{primaryMod}}+Enter 发送消息",
+			"description": "启用后,必须按 {{primaryMod}}+Enter 发送消息,而不仅仅是 Enter"
 		}
 	}
 }

+ 1 - 0
webview-ui/src/i18n/locales/zh-TW/chat.json

@@ -125,6 +125,7 @@
 	"enhancePromptDescription": "「強化提示詞」按鈕可透過提供額外內容、說明或改寫來協助改善您的提示詞。請試著在這裡輸入提示詞,然後再次點選按鈕,以了解其運作方式。",
 	"addImages": "新增圖片到訊息中",
 	"sendMessage": "傳送訊息",
+	"pressToSend": "按 {{keyCombination}} 傳送",
 	"stopTts": "停止文字轉語音",
 	"typeMessage": "輸入訊息...",
 	"typeTask": "在這裡輸入您的工作...",

+ 6 - 2
webview-ui/src/i18n/locales/zh-TW/settings.json

@@ -953,8 +953,12 @@
 	},
 	"ui": {
 		"collapseThinking": {
-			"label": "預設折疊“思考”訊息",
-			"description": "啟用後,“思考”塊將預設折疊,直到您與其互動"
+			"label": "預設折疊「思考」訊息",
+			"description": "啟用後,「思考」塊將預設折疊,直到您與其互動"
+		},
+		"requireCtrlEnterToSend": {
+			"label": "需要 {{primaryMod}}+Enter 傳送訊息",
+			"description": "啟用後,必須按 {{primaryMod}}+Enter 傳送訊息,而不只是 Enter"
 		}
 	}
 }