fix: improve text selection handling in copy handler for message formatting
This commit is contained in:
@@ -28,54 +28,69 @@ export const setupCopyHandler = (
|
|||||||
const messages: Array<{ sender: string; time: string; content: string }> = [];
|
const messages: Array<{ sender: string; time: string; content: string }> = [];
|
||||||
|
|
||||||
// Get all message elements in the container
|
// Get all message elements in the container
|
||||||
const messageElements = container.querySelectorAll('[data-message-id]');
|
const messageElements = Array.from(container.querySelectorAll('[data-message-id]'));
|
||||||
|
|
||||||
messageElements.forEach((msgEl) => {
|
messageElements.forEach((msgEl) => {
|
||||||
// Check if this message element intersects with the selection
|
|
||||||
if (!selection.containsNode(msgEl, true)) return;
|
|
||||||
|
|
||||||
const eventId = msgEl.getAttribute('data-message-id');
|
const eventId = msgEl.getAttribute('data-message-id');
|
||||||
if (!eventId) return;
|
if (!eventId) return;
|
||||||
|
|
||||||
const data = getMessageData(eventId);
|
const data = getMessageData(eventId);
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
// Get the selected text content within this message
|
// Find content elements within the message (skip headers, timestamps, etc.)
|
||||||
const tempRange = document.createRange();
|
const contentEl = msgEl.querySelector('[class*="MessageTextBody"], [class*="message-content"], [class*="MessageBody"]') as HTMLElement;
|
||||||
tempRange.selectNodeContents(msgEl);
|
|
||||||
|
|
||||||
// Check if the selection overlaps with this message
|
if (!contentEl) return;
|
||||||
const intersects = range.compareBoundaryPoints(Range.END_TO_START, tempRange) <= 0 &&
|
|
||||||
range.compareBoundaryPoints(Range.START_TO_END, tempRange) >= 0;
|
// Check if the content element intersects with the selection
|
||||||
|
// by checking if selection contains any part of it
|
||||||
if (!intersects) return;
|
let isSelected = false;
|
||||||
|
try {
|
||||||
|
// Try to detect if this node or its children are in the selection
|
||||||
|
const walker = document.createTreeWalker(
|
||||||
|
contentEl,
|
||||||
|
NodeFilter.SHOW_TEXT,
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
let textNode = walker.nextNode();
|
||||||
|
while (textNode && !isSelected) {
|
||||||
|
if (range.intersectsNode(textNode)) {
|
||||||
|
isSelected = true;
|
||||||
|
}
|
||||||
|
textNode = walker.nextNode();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Fallback: check if selection contains the content element
|
||||||
|
isSelected = selection.containsNode(contentEl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isSelected) return;
|
||||||
|
|
||||||
// Format the time
|
// Format the time
|
||||||
const timeStr = timeHourMinute(data.ts, hour24Clock);
|
const timeStr = timeHourMinute(data.ts, hour24Clock);
|
||||||
|
|
||||||
// Get the text content that was selected from this message
|
// Extract the selected text from within this message's content
|
||||||
// Use the original content if the message is fully selected, otherwise get selected portion
|
|
||||||
let selectedText = '';
|
let selectedText = '';
|
||||||
|
|
||||||
// Find the message content element (the actual text, not header)
|
// Create a range for this message's content
|
||||||
const contentElement = msgEl.querySelector('[class*="MessageTextBody"], [class*="message-content"]');
|
const contentRange = document.createRange();
|
||||||
if (contentElement) {
|
contentRange.selectNodeContents(contentEl);
|
||||||
const clonedRange = range.cloneRange();
|
|
||||||
clonedRange.selectNodeContents(contentElement);
|
// Clone the selection range to avoid modifying it
|
||||||
|
const extractRange = range.cloneRange();
|
||||||
// Intersect with selection
|
|
||||||
if (range.compareBoundaryPoints(Range.START_TO_START, clonedRange) > 0) {
|
// Constrain the extract range to be within the content element
|
||||||
clonedRange.setStart(range.startContainer, range.startOffset);
|
// If selection starts before content, move start to content start
|
||||||
}
|
if (contentRange.compareBoundaryPoints(Range.START_TO_START, extractRange) > 0) {
|
||||||
if (range.compareBoundaryPoints(Range.END_TO_END, clonedRange) < 0) {
|
extractRange.setStart(contentRange.startContainer, contentRange.startOffset);
|
||||||
clonedRange.setEnd(range.endContainer, range.endOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedText = clonedRange.toString().trim();
|
|
||||||
} else {
|
|
||||||
// Fallback: use all selected text in this message
|
|
||||||
selectedText = range.toString().trim();
|
|
||||||
}
|
}
|
||||||
|
// If selection ends after content, move end to content end
|
||||||
|
if (contentRange.compareBoundaryPoints(Range.END_TO_END, extractRange) < 0) {
|
||||||
|
extractRange.setEnd(contentRange.endContainer, contentRange.endOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedText = extractRange.toString().trim();
|
||||||
|
|
||||||
if (selectedText) {
|
if (selectedText) {
|
||||||
messages.push({
|
messages.push({
|
||||||
|
|||||||
Reference in New Issue
Block a user