import { Extension } from '@tiptap/core' import { Plugin, PluginKey, NodeSelection } from '@tiptap/pm/state' import { Decoration, DecorationSet } from '@tiptap/pm/view' /** * Extension ProseMirror/TipTap qui ajoute la classe CSS 'block-selected' * aux blocs de premier niveau inclus ou traversés par la sélection active. */ export const BlockSelectionExtension = Extension.create({ name: 'blockSelection', addProseMirrorPlugins() { return [ new Plugin({ key: new PluginKey('blockSelection'), props: { decorations(state) { const { from, to, empty } = state.selection if (empty) return DecorationSet.empty const decorations: Decoration[] = [] // Parcourir uniquement les nœuds de premier niveau (enfants directs de doc) state.doc.forEach((node, offset) => { const start = offset const end = offset + node.nodeSize // Vérifier si la plage de sélection [from, to] intersecte le bloc [start, end] const intersects = Math.max(start, from) < Math.min(end, to) if (intersects) { // Il faut s'assurer que c'est un nœud de bloc pour appliquer Decoration.node if (node.isBlock) { try { decorations.push( Decoration.node(start, end, { class: 'block-selected', }) ) } catch (e) { // Fallback en cas d'erreur sur des structures de nœuds particulières } } } }) const isNodeSel = state.selection instanceof NodeSelection // Si la sélection s'étend sur plusieurs blocs ou si c'est une NodeSelection explicite, // on renvoie l'ensemble des décorations pour coloration visuelle. if (decorations.length > 1 || (decorations.length === 1 && isNodeSel)) { return DecorationSet.create(state.doc, decorations) } return DecorationSet.empty }, }, }), ] }, })