fix(BlockEditor): constrain select-all to current block and resize slash menu

- intercept Ctrl/Cmd+A to select only the current block's content, preventing cross-block merge bug
- increase slash menu width from 280 to 375 and max-height from 320 to 360
- enlarge icon container, increase gap and padding, and upsize text in slash menu items
This commit is contained in:
2026-04-25 17:53:16 -04:00
parent 489147d25d
commit 9df91bf412
2 changed files with 21 additions and 6 deletions
@@ -120,6 +120,21 @@ const Block = forwardRef(function Block(
return; return;
} }
// Ctrl/Cmd+A : limite la sélection au bloc courant. La sélection native
// s'étend sur plusieurs contentEditable et leur suppression fusionne le
// texte en un seul bloc — bug. On force la sélection à rester ici.
if ((e.ctrlKey || e.metaKey) && (e.key === 'a' || e.key === 'A')) {
if (el) {
e.preventDefault();
const range = document.createRange();
range.selectNodeContents(el);
const sel = window.getSelection();
sel?.removeAllRanges();
sel?.addRange(range);
}
return;
}
if (e.key === 'Enter' && !e.shiftKey) { if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault(); e.preventDefault();
const el = editableRef.current; const el = editableRef.current;
@@ -2,8 +2,8 @@
import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
const MENU_WIDTH = 280; const MENU_WIDTH = 375;
const MENU_MAX_HEIGHT = 320; const MENU_MAX_HEIGHT = 360;
const VIEWPORT_MARGIN = 8; const VIEWPORT_MARGIN = 8;
const SHORTCUT_HINT = { const SHORTCUT_HINT = {
@@ -135,16 +135,16 @@ export default function SlashMenu({
data-slash-index={i} data-slash-index={i}
onMouseEnter={() => onHoverIndex?.(i)} onMouseEnter={() => onHoverIndex?.(i)}
onClick={() => onSelect?.(def.type)} onClick={() => onSelect?.(def.type)}
className={`w-full flex items-center gap-2.5 px-2 py-1 text-left transition-colors ${active ? 'bg-neutral-100 dark:bg-neutral-800' : ''}`} className={`w-full flex items-center gap-3 px-2 py-1.5 text-left transition-colors ${active ? 'bg-neutral-100 dark:bg-neutral-800' : ''}`}
> >
<span className="w-6 h-6 flex items-center justify-center rounded-[5px] border border-neutral-200 dark:border-neutral-700 text-[11px] font-medium text-neutral-700 dark:text-neutral-300 flex-shrink-0"> <span className="w-8 h-8 flex items-center justify-center rounded-md border border-neutral-200 dark:border-neutral-700 text-xs font-medium text-neutral-700 dark:text-neutral-300 flex-shrink-0">
{def.icon} {def.icon}
</span> </span>
<span className="flex-1 min-w-0 truncate text-[13px] text-neutral-900 dark:text-white"> <span className="flex-1 min-w-0 truncate text-sm text-neutral-900 dark:text-white">
{def.label} {def.label}
</span> </span>
{hint && ( {hint && (
<span className="text-[11px] font-mono text-neutral-400 dark:text-neutral-500 flex-shrink-0"> <span className="text-xs font-mono text-neutral-400 dark:text-neutral-500 flex-shrink-0">
{hint} {hint}
</span> </span>
)} )}