diff --git a/js/content.js b/js/content.js index 7bfc439..b11a71c 100644 --- a/js/content.js +++ b/js/content.js @@ -1,10 +1,21 @@ (function () { + // Function to log debug messages if debugging is enabled. + function logDebug(...args) { + const debug = false; // Set to true to enable debug logging + if (debug) { + console.debug(...args); + } + } + // Prevents script from running multiple times. if (window.hasRun) { + logDebug("Script has already run, exiting..."); return; } window.hasRun = true; + logDebug("Script started and running for the first time."); + /** * Selectors for identifying the "New Message" button. * This covers various layouts or attributes of the button. @@ -21,6 +32,8 @@ "button svg[width='32'][height='32'][viewBox='0 0 32 32']" // Specific SVG inside the button ]; + logDebug("New Message Button Selectors initialized:", newMessageButtonSelectors); + /** * Selectors for identifying the "Edit Message" button. * Like `newMessageButtonSelectors`, it covers different possible layouts. @@ -38,36 +51,56 @@ "button.btn.btn-primary.relative" // Button by class names ]; + logDebug("Edit Message Button Selectors initialized:", editMessageButtonSelectors); + /** * Inserts a line break into a text field or contenteditable element. * @param {HTMLElement} inputField - The active element for inserting the line break. */ function insertLineBreak(inputField) { - if (!inputField) return; + logDebug("Attempting to insert line break. Element:", inputField); + + if (!inputField) { + console.warn("No input field provided, aborting line break insertion."); + return; + } + + const tagName = inputField.tagName.toLowerCase(); + logDebug("Input field tagName:", tagName); // Handle textarea and input elements if (inputField.tagName.toLowerCase() === 'textarea' || inputField.tagName.toLowerCase() === 'input') { - console.debug('Inserting a line break into an input or textarea.'); + logDebug('Inserting a line break into an input or textarea.'); // Insert the line break at the cursor position const cursorPos = inputField.selectionStart; const text = inputField.value; + logDebug("Current cursor position:", cursorPos, "Current text length:", text.length); + inputField.value = `${text.slice(0, cursorPos)}\n${text.slice(cursorPos)}`; inputField.selectionStart = inputField.selectionEnd = cursorPos + 1; + logDebug("Line break inserted. Updated text:", inputField.value); + // Trigger input event to ensure UI updates const event = new Event('input', { bubbles: true }); inputField.dispatchEvent(event); + logDebug("Input event dispatched after line break insertion."); } else if (inputField.isContentEditable) { // Handle contenteditable elements - console.debug('Inserting a line break into a contenteditable element.'); + logDebug('Inserting a line break into a contenteditable element.'); const selection = window.getSelection(); - if (!selection.rangeCount) return; + if (!selection.rangeCount) { + console.warn("No range found in selection, aborting line break insertion."); + return; + } const range = selection.getRangeAt(0); + logDebug("Current selection range:", range); + const br = document.createElement("br"); // Insert the
tag at the cursor position @@ -76,63 +109,73 @@ range.setStartAfter(br); range.setEndAfter(br); + logDebug("Line break (
) inserted at cursor position."); + // Update the cursor position selection.removeAllRanges(); selection.addRange(range); + logDebug("Cursor position updated after line break insertion."); } } /** * Searches for the closest "Send" or "Edit" button in parent containers. + * This function traverses up the DOM tree from the active element and attempts to find + * the closest button that matches either the "Edit Message" or "New Message" selectors. + * The search is limited by the maximum number of levels specified by `maxSearchLevels`. + * * @param {HTMLElement} activeElement - The currently active element. - * @returns {HTMLElement|null} - The matched button or null if none is found. + * @returns {HTMLElement|null} - The matched button (either "Edit Message" or "New Message") or null if none is found. */ function findClosestButton(activeElement) { + logDebug("Starting search for closest button. Active element:", activeElement); + let parent = activeElement; - const maxSearchLevels = 5; + const maxSearchLevels = 5; // Limit the search depth in the DOM tree let currentLevel = 0; let matchingButton = null; - let buttonType = ''; // 'EditMessage' or 'NewMessage' - let totalMatches = 0; - - console.debug('Searching for the closest button in parent elements.'); + let buttonType = ''; // To store the type of button found: 'EditMessage' or 'NewMessage' + let totalMatches = 0; // To track the number of matches found during traversal - // Traverse up the DOM tree to find a button + // Traverse up the DOM tree to find a button within the specified number of levels while (parent && currentLevel < maxSearchLevels) { currentLevel++; - console.debug(`Searching at level ${currentLevel}.`); + logDebug(`Searching at level ${currentLevel}. Current parent element:`, parent); - // Check for "Edit Message" buttons + // Check for "Edit Message" buttons in the current parent element editMessageButtonSelectors.forEach((selector, index) => { const button = parent.querySelector(selector); if (button) { - console.debug(`Found "Edit Message" button using selector ${selector}.`); + logDebug(`Found "Edit Message" button using selector [${index}]: ${selector}.`); matchingButton = button; buttonType = 'EditMessage'; totalMatches++; } }); - // Check for "New Message" buttons + // Check for "New Message" buttons in the current parent element newMessageButtonSelectors.forEach((selector, index) => { const button = parent.querySelector(selector); - if (button) { - console.debug(`Found "New Message" button using selector ${selector}.`); + if (button && button.matches('button[data-testid="send-button"]')) { + logDebug(`Found "New Message" button using selector [${index}]: ${selector}.`); matchingButton = button; buttonType = 'NewMessage'; totalMatches++; } }); + // If a matching button was found, return the one with the highest matches if (matchingButton) { - console.debug(`Found button with highest matches (${totalMatches}). Type: ${buttonType}`); + logDebug(`Found button with highest matches (${totalMatches}). Type: ${buttonType}`); return matchingButton; } - parent = parent.parentElement; // Move up to the parent element + // Move up the DOM tree to the parent element and continue searching + parent = parent.parentElement; } + // Log a warning if no matching button was found after the search console.warn('Button not found after checking parent elements.'); return null; } @@ -143,7 +186,7 @@ */ function simulateMouseClick(button) { if (button) { - console.debug('Simulating mouse click on the button.'); + logDebug('Simulating mouse click on the button.', button); const mousedownEvent = new MouseEvent('mousedown', { bubbles: true, @@ -166,40 +209,72 @@ button.dispatchEvent(mouseupEvent); button.dispatchEvent(clickEvent); - console.debug("Mouse click simulation completed."); + logDebug("Mouse click simulation completed for the button:", button); } else { console.warn("Button for click simulation not found."); } } /** - * Keydown event handler to intercept Enter and Ctrl+Enter key presses. - * - Inserts a line break if Enter is pressed without Ctrl/Alt. - * - Sends the message if Ctrl+Enter is pressed. + * Keydown event handler to intercept Enter, Ctrl+Enter, and Shift+Enter key presses. + * - Sends the message if Ctrl+Enter is pressed by finding and simulating a click on the send button. + * - Inserts a line break when Enter or Shift+Enter is pressed in a contenteditable element or textarea. + * - Prevents the default behavior to handle these key actions manually. + * @param {KeyboardEvent} event - The keyboard event triggered by pressing a key. */ document.addEventListener('keydown', function (event) { - if (event.key === 'Enter') { - const activeElement = document.activeElement; - - // Insert a line break on Enter without Ctrl or Alt - if (!event.ctrlKey && !event.altKey) { - event.preventDefault(); - console.debug("Inserting line break into active element."); - insertLineBreak(activeElement); + // Define the keys that are monitored by this event listener + const allowedKeys = ['Enter', 'Control', 'Shift']; + + // Ignore other key presses and handle only specified key actions + if (!allowedKeys.includes(event.key)) { + return; // Ignore all other key presses + } + + // Get the active element to process it + const activeElement = document.activeElement; + + logDebug(`Keydown event detected. Key: ${event.key}, CtrlKey: ${event.ctrlKey}, ShiftKey: ${event.shiftKey}, AltKey: ${event.altKey}`); + + // If Ctrl+Enter is pressed, send the message by simulating a click on the send button + if (event.key === 'Enter' && event.ctrlKey) { + event.preventDefault(); + logDebug("Ctrl+Enter detected, preparing to send the message."); + + // Search for the closest send button and simulate a mouse click + const button = findClosestButton(activeElement); + if (button) { + simulateMouseClick(button); + } else { + console.warn("Message send button not found."); } + } + // If Enter or Shift+Enter is pressed, insert a line break manually using insertLineBreak + else if (event.key === 'Enter' && !event.ctrlKey && !event.altKey && !event.__isSimulated) { + event.preventDefault(); + logDebug("Enter or Shift+Enter detected, inserting line break manually."); - // Send the message on Ctrl+Enter - if (event.ctrlKey) { - event.preventDefault(); - console.debug("Searching for the send button with Ctrl+Enter."); + // If the active element is contenteditable, simulate Shift+Enter to insert a line break + if (activeElement && activeElement.isContentEditable) { + const shiftEnterEvent = new KeyboardEvent('keydown', { + key: 'Enter', + code: 'Enter', + keyCode: 13, + which: 13, + shiftKey: true, + bubbles: true, + cancelable: true + }); - const button = findClosestButton(activeElement); - if (button) { - simulateMouseClick(button); - } else { - console.warn("Message send button not found."); - } + // Set a flag to avoid cyclic behavior + Object.defineProperty(shiftEnterEvent, '__isSimulated', { value: true }); + + event.target.dispatchEvent(shiftEnterEvent); // Simulate Shift+Enter key press + } else if (activeElement.tagName.toLowerCase() === 'textarea' || activeElement.tagName.toLowerCase() === 'input') { + insertLineBreak(activeElement); // For textarea or input elements } } }, true); + + logDebug("Event listener for keydown events initialized."); })(); diff --git a/manifest.json b/manifest.json index fb584ef..cd1e331 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "__MSG_extensionName__", "description": "__MSG_extensionDescription__", "default_locale": "en", - "version": "2024.09.13", + "version": "2024.09.29", "permissions": [ "activeTab", "scripting"