Skip to content

Commit ae7fcc4

Browse files
committed
Fix select all when inside jupyter cells
1 parent e57291d commit ae7fcc4

File tree

1 file changed

+15
-84
lines changed

1 file changed

+15
-84
lines changed

packages/lexical/src/plugins/JupyterInputOutputPlugin.tsx

Lines changed: 15 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
COMMAND_PRIORITY_EDITOR,
1414
INSERT_LINE_BREAK_COMMAND,
1515
COMMAND_PRIORITY_HIGH,
16+
COMMAND_PRIORITY_CRITICAL,
1617
KEY_ENTER_COMMAND,
1718
COMMAND_PRIORITY_LOW,
1819
$createLineBreakNode,
@@ -41,7 +42,6 @@ import {
4142
JupyterInputNode,
4243
$isJupyterInputNode,
4344
} from '../nodes/JupyterInputNode';
44-
import { $isJupyterInputHighlightNode } from '../nodes/JupyterInputHighlightNode';
4545
import { registerCodeHighlighting } from '../nodes/JupyterInputHighlighter';
4646
import {
4747
JupyterOutputNode,
@@ -425,101 +425,36 @@ export const JupyterInputOutputPlugin = (
425425
}, [editor, kernel]);
426426

427427
// Handle Ctrl+A to select all content within a Jupyter input cell
428-
// Use Lexical command system with HIGH priority to intercept before default behavior
428+
// Use CRITICAL priority to run before Lexical's internal select-all handler
429429
useEffect(() => {
430430
return editor.registerCommand<KeyboardEvent>(
431431
SELECT_ALL_COMMAND,
432432
(event: KeyboardEvent) => {
433-
console.log(
434-
'🎯 SELECT_ALL_COMMAND intercepted before default behavior',
435-
);
436-
437433
const selection = $getSelection();
438434
if (!$isRangeSelection(selection)) {
439-
console.log('❌ No range selection available');
440-
return false; // Let default behavior handle it
435+
return false;
441436
}
442437

443-
// Debug: Log current selection details BEFORE default Ctrl+A
444-
console.log('📍 Current selection details (BEFORE default Ctrl+A):', {
445-
anchorKey: selection.anchor.key,
446-
anchorOffset: selection.anchor.offset,
447-
anchorType: selection.anchor.type,
448-
focusKey: selection.focus.key,
449-
focusOffset: selection.focus.offset,
450-
focusType: selection.focus.type,
451-
});
452-
453-
// Start from the anchor node and traverse up the hierarchy
454-
const anchorNode = selection.anchor.getNode();
455-
console.log('🔍 Anchor node details (BEFORE default Ctrl+A):', {
456-
type: anchorNode.getType(),
457-
key: anchorNode.getKey(),
458-
textContent: anchorNode.getTextContent?.()?.slice(0, 50) || 'N/A',
459-
parent: anchorNode.getParent()?.getType() || 'No parent',
460-
});
461-
462-
// Also check the focus node in case it's different
463-
const focusNode = selection.focus.getNode();
464-
if (focusNode !== anchorNode) {
465-
console.log('🔍 Focus node (different from anchor):', {
466-
type: focusNode.getType(),
467-
key: focusNode.getKey(),
468-
textContent: focusNode.getTextContent?.()?.slice(0, 50) || 'N/A',
469-
parent: focusNode.getParent()?.getType() || 'No parent',
470-
});
471-
}
472-
473-
let currentNode: LexicalNode | null = anchorNode;
438+
// Traverse up from current node to find parent JupyterInputNode
439+
let currentNode: LexicalNode | null = selection.anchor.getNode();
474440
let jupyterInputNode: JupyterInputNode | null = null;
475441
let depth = 0;
476-
const maxDepth = 20; // Prevent infinite loops
442+
const maxDepth = 20;
477443

478-
// Traverse up the tree hierarchy
479444
while (currentNode && depth < maxDepth) {
480-
const nodeType = currentNode.getType();
481-
const nodeKey = currentNode.getKey();
482-
const isJupyterInput = $isJupyterInputNode(currentNode);
483-
const isJupyterHighlight = $isJupyterInputHighlightNode(currentNode);
484-
485-
console.log(
486-
`🔍 Depth ${depth}: ${nodeType} (${nodeKey}) - isJupyterInput: ${isJupyterInput}, isJupyterHighlight: ${isJupyterHighlight}`,
487-
);
488-
489-
// Direct check: Is this node a JupyterInputNode?
490-
if (isJupyterInput) {
445+
if ($isJupyterInputNode(currentNode)) {
491446
jupyterInputNode = currentNode as JupyterInputNode;
492-
console.log(`🎯 SUCCESS: Found JupyterInputNode at depth ${depth}`);
493447
break;
494448
}
495449

496-
// If this is a JupyterInputHighlightNode, continue traversing up
497-
// (the parent should be a JupyterInputNode)
498-
if (isJupyterHighlight) {
499-
console.log(
500-
`🔍 Found JupyterInputHighlightNode at depth ${depth}, continuing traversal...`,
501-
);
502-
}
503-
504-
// Move to parent
505-
const parentNode: LexicalNode | null = currentNode.getParent();
506-
if (parentNode) {
507-
currentNode = parentNode;
508-
depth++;
509-
} else {
510-
console.log(`🔍 No parent at depth ${depth}, reached root`);
511-
break;
512-
}
513-
}
514-
515-
if (depth >= maxDepth) {
516-
console.warn(`🔍 Max depth ${maxDepth} reached, stopping traversal`);
450+
currentNode = currentNode.getParent();
451+
depth++;
517452
}
518453

519454
if (jupyterInputNode) {
520-
console.log(
521-
'🎯 SUCCESS: Found JupyterInputNode, selecting all content within cell',
522-
);
455+
// Prevent default browser behavior only when we're handling the event
456+
event.preventDefault();
457+
event.stopPropagation();
523458

524459
// Select all content within the Jupyter input node
525460
const rangeSelection = $createRangeSelection();
@@ -530,16 +465,12 @@ export const JupyterInputOutputPlugin = (
530465
'element',
531466
);
532467
$setSelection(rangeSelection);
533-
534-
return true; // Prevent default Ctrl+A behavior
468+
return true; // Prevent default select-all behavior
535469
}
536470

537-
console.log(
538-
'❌ No JupyterInputNode found in hierarchy - allowing default Ctrl+A',
539-
);
540-
return false; // Let default behavior handle it
471+
return false; // Allow default select-all if not in Jupyter cell
541472
},
542-
COMMAND_PRIORITY_HIGH, // HIGH priority to intercept before default behavior
473+
COMMAND_PRIORITY_CRITICAL,
543474
);
544475
}, [editor]);
545476

0 commit comments

Comments
 (0)