diff --git a/src/shared/components/BlockEditor/BlockEditor.client.js b/src/shared/components/BlockEditor/BlockEditor.client.js
index c5abadc..4c46469 100644
--- a/src/shared/components/BlockEditor/BlockEditor.client.js
+++ b/src/shared/components/BlockEditor/BlockEditor.client.js
@@ -13,6 +13,7 @@ import {
sliceInline,
concatInline,
toggleMark,
+ removeAllMarks,
marksInRange,
collectUsedColors,
} from './inline/types.js';
@@ -509,7 +510,20 @@ export default function BlockEditor({
const next = toggleMark(block.content ?? [], start, end, mark);
const nextBlocks = blocks.map(b => (b.id === blockId ? { ...b, content: next } : b));
commitChange(nextBlocks, { immediate: true });
- // Restaure la sélection après le re-render.
+ requestAnimationFrame(() => {
+ const ref = blockRefs.current.get(blockId);
+ ref?.setCaretRange?.(start, end);
+ });
+ }
+
+ function applyRemoveAllMarks() {
+ if (!toolbar) return;
+ const { blockId, start, end } = toolbar;
+ const block = blocks.find(b => b.id === blockId);
+ if (!block) return;
+ const next = removeAllMarks(block.content ?? [], start, end);
+ const nextBlocks = blocks.map(b => (b.id === blockId ? { ...b, content: next } : b));
+ commitChange(nextBlocks, { immediate: true });
requestAnimationFrame(() => {
const ref = blockRefs.current.get(blockId);
ref?.setCaretRange?.(start, end);
@@ -851,6 +865,7 @@ export default function BlockEditor({
activeMarks={marks}
usedColors={usedColors}
onToggleMark={applyToggleMark}
+ onClearMarks={applyRemoveAllMarks}
onPinChange={(p) => { toolbarPinnedRef.current = p; }}
/>
);
diff --git a/src/shared/components/BlockEditor/README.md b/src/shared/components/BlockEditor/README.md
index f979768..e322cd1 100644
--- a/src/shared/components/BlockEditor/README.md
+++ b/src/shared/components/BlockEditor/README.md
@@ -75,7 +75,7 @@ Le contenu vide est `[]` (jamais `[{type:'text', text:''}]`).
```js
import {
inlineLength, inlineToPlainText, inlineFromText,
- sliceInline, concatInline, applyMark, toggleMark,
+ sliceInline, concatInline, applyMark, toggleMark, removeAllMarks,
marksAtOffset, marksInRange, INLINE_COLORS,
isHexColor, collectUsedColors,
} from '@zen/core/shared/components/BlockEditor';
@@ -126,6 +126,7 @@ apparaît au-dessus. Il propose :
``)
- **◐** — surlignage (même structure de popover)
- **🔗** — lien (popover avec input URL ; ✕ pour retirer)
+- **T/** — effacer tout le formatage de la sélection (supprime toutes les marks)
L'état actif est calculé à partir des marks **communes à toute la plage**
(via `marksInRange`). Toggle off si toute la plage est déjà marquée.
diff --git a/src/shared/components/BlockEditor/inline/Toolbar.client.js b/src/shared/components/BlockEditor/inline/Toolbar.client.js
index 16187d4..75e6196 100644
--- a/src/shared/components/BlockEditor/inline/Toolbar.client.js
+++ b/src/shared/components/BlockEditor/inline/Toolbar.client.js
@@ -1,7 +1,7 @@
'use client';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
-import { TextColorIcon, HighlighterIcon, Link02Icon, CodeSimpleIcon } from '@zen/core/shared/icons';
+import { TextColorIcon, HighlighterIcon, Link02Icon, CodeSimpleIcon, TextClearIcon } from '@zen/core/shared/icons';
import { INLINE_COLORS, INLINE_COLOR_KEYS, markKey } from './types.js';
// Toolbar flottant de formatage. Affiché tant qu'une sélection non-vide
@@ -23,7 +23,7 @@ const SIMPLE_BUTTONS = [
{ type: 'code', label: , title: 'Code (Ctrl+E)', className: '' },
];
-export default function InlineToolbar({ rect, activeMarks, onToggleMark, onPinChange, usedColors }) {
+export default function InlineToolbar({ rect, activeMarks, onToggleMark, onClearMarks, onPinChange, usedColors }) {
const ref = useRef(null);
const [pos, setPos] = useState({ top: 0, left: 0, flipped: false });
const [popover, setPopover] = useState(null); // 'color' | 'highlight' | 'link' | null
@@ -146,6 +146,18 @@ export default function InlineToolbar({ rect, activeMarks, onToggleMark, onPinCh
+
+
+
+
{popover === 'color' && (
addMarkToNode(n, mark));
}
+export function removeAllMarks(nodes, start, end) {
+ return mapRange(nodes, start, end, n => makeNode(n.text));
+}
+
export function removeMark(nodes, start, end, type) {
return mapRange(nodes, start, end, n => removeMarkFromNode(n, type));
}
diff --git a/src/shared/icons/index.js b/src/shared/icons/index.js
index 23bdfeb..7f14cee 100644
--- a/src/shared/icons/index.js
+++ b/src/shared/icons/index.js
@@ -692,4 +692,11 @@ export const CodeSimpleIcon = (props) => (
-);
\ No newline at end of file
+);
+
+export const TextClearIcon = (props) => (
+
+);