mirror of
https://github.com/chatwoot/chatwoot.git
synced 2026-06-13 21:01:16 +08:00
# Pull Request Template ## Description This PR includes, On Windows, pressing **Ctrl+Enter** in the reply editor was inserting an unintended line break before sending. This led to two issues: * **Unexpected blank lines** After adding a line break with Shift+Enter and removing it with Backspace, the editor looked correct. However, sending with Ctrl+Enter reintroduced a hidden break, resulting in an extra blank line in the final message. * **Selected text being replaced** When text was selected and Ctrl+Enter was pressed, the selection was replaced with a line break instead of being sent. Fixes https://linear.app/chatwoot/issue/CW-6840/newline-bug-in-the-editor ### **Cause** Two keyboard handlers responded to **Ctrl+Enter** on Windows: * ProseMirror (`Mod-Enter`) inserted a hard break * ReplyBox (`$mod+Enter`) triggered send The existing guard only checked `metaKey` (Cmd), so it never worked on Windows. As a result, a line break was inserted just before sending. ### **Solution** Make the modifier check platform-aware so the editor correctly intercepts the send shortcut: * Added `detectOS`, `isMac`, and `OS` constants * Introduced `hasPressedMod` (uses `metaKey` on macOS, `ctrlKey` elsewhere) This ensures Ctrl+Enter sends the message without modifying content, while keeping existing behavior unchanged. **NB:** macOS behavior with Cmd+Enter remains unchanged ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? **Case 1: line break** 1. Type `hello` 2. Press Shift+Enter, then Backspace 3. Press Ctrl+Enter → Message contains an unexpected blank new line **Case 2: Selection replaced** 1. Type two lines using Shift+Enter 2. Select text on the second line 3. Press Ctrl+Enter → Selected text is replaced and not sent ### Screencast **Before** https://github.com/user-attachments/assets/d6d285a9-260b-4711-8bbd-d0c8519e8d20 **After** https://github.com/user-attachments/assets/c0ace1f7-5d22-44a2-8e08-22190ee21e61 ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
68 lines
2.0 KiB
JavaScript
68 lines
2.0 KiB
JavaScript
import { isApple } from './platform';
|
|
|
|
export const isEnter = e => {
|
|
return e.key === 'Enter';
|
|
};
|
|
|
|
export const isEscape = e => {
|
|
return e.key === 'Escape';
|
|
};
|
|
|
|
export const hasPressedShift = e => {
|
|
return e.shiftKey;
|
|
};
|
|
|
|
export const hasPressedCommand = e => {
|
|
return e.metaKey;
|
|
};
|
|
|
|
// True when the platform's "command" modifier is held: Cmd (metaKey) on
|
|
// Apple platforms (macOS, iOS/iPadOS hardware keyboards), Ctrl (ctrlKey)
|
|
// elsewhere. Mirrors the `$mod` convention used by tinykeys and
|
|
// prosemirror-keymap so the editor and the app agree on what counts as the
|
|
// send modifier.
|
|
export const hasPressedMod = e => Boolean(isApple() ? e.metaKey : e.ctrlKey);
|
|
|
|
export const hasPressedEnterAndNotCmdOrShift = e => {
|
|
return isEnter(e) && !hasPressedMod(e) && !hasPressedShift(e);
|
|
};
|
|
|
|
// Detects the platform-aware "send" shortcut: Cmd+Enter on Apple platforms,
|
|
// Ctrl+Enter on Windows/Linux.
|
|
export const hasPressedCommandAndEnter = e => hasPressedMod(e) && isEnter(e);
|
|
|
|
// If layout is QWERTZ then we add the Shift+keysToModify to fix an known issue
|
|
// https://github.com/chatwoot/chatwoot/issues/9492
|
|
export const keysToModifyInQWERTZ = new Set(['Alt+KeyP', 'Alt+KeyL']);
|
|
|
|
export const LAYOUT_QWERTY = 'QWERTY';
|
|
export const LAYOUT_QWERTZ = 'QWERTZ';
|
|
export const LAYOUT_AZERTY = 'AZERTY';
|
|
|
|
/**
|
|
* Determines whether the active element is typeable.
|
|
*
|
|
* @param {KeyboardEvent} e - The keyboard event object.
|
|
* @returns {boolean} `true` if the active element is typeable, `false` otherwise.
|
|
*
|
|
* @example
|
|
* document.addEventListener('keydown', e => {
|
|
* if (isActiveElementTypeable(e)) {
|
|
* handleTypeableElement(e);
|
|
* }
|
|
* });
|
|
*/
|
|
export const isActiveElementTypeable = e => {
|
|
/** @type {HTMLElement | null} */
|
|
// @ts-ignore
|
|
const activeElement = e.target || document.activeElement;
|
|
|
|
return !!(
|
|
activeElement?.tagName === 'INPUT' ||
|
|
activeElement?.tagName === 'NINJA-KEYS' ||
|
|
activeElement?.tagName === 'TEXTAREA' ||
|
|
activeElement?.contentEditable === 'true' ||
|
|
activeElement?.className?.includes('ProseMirror')
|
|
);
|
|
};
|