I have a custom colorSyntax plugin wrapper:
function colorSyntax(context: PluginContext, options: PluginOptions): PluginInfo {
const pluginInfo = originalColorSyntax(context, options);
if (pluginInfo.wysiwygCommands?.color) {
pluginInfo.wysiwygCommands.color = (value, state, dispatch) => {
const { selectedColor } = value as any;
if (!selectedColor) return false;
const { tr, selection, schema } = state as any;
const hasRange = !selection.empty;
selection.ranges.forEach((range: any) => {
let start = range.$from.pos;
let end = range.$to.pos;
let pos = start;
let prevStyle = null;
let spanStart = pos;
while (pos < end) {
const node = tr.doc.nodeAt(pos);
if (node && node.isText) {
const marks = node.marks || [];
const spanMark = marks.find((m: any) => m.type === schema.marks.span);
const style = spanMark?.attrs.htmlAttrs?.style || '';
if (prevStyle !== null && style !== prevStyle) {
const mergedStyle = prevStyle.replace(/(?:^|;)\s*color\s*:[^;]+;?/i, '').trim();
const finalStyle = `${mergedStyle ? mergedStyle + ';' : ''}color: ${selectedColor};`;
const attrs = { htmlAttrs: { style: finalStyle } };
tr.addMark(spanStart, pos, schema.marks.span.create(attrs));
spanStart = pos;
}
prevStyle = style;
pos += node.nodeSize;
} else {
pos += 1;
}
}
if (prevStyle !== null && spanStart < end) {
const mergedStyle = prevStyle.replace(/(?:^|;)\s*color\s*:[^;]+;?/i, '').trim();
const finalStyle = `${mergedStyle ? mergedStyle + ';' : ''}color: ${selectedColor};`;
const attrs = { htmlAttrs: { style: finalStyle } };
tr.addMark(spanStart, end, schema.marks.span.create(attrs));
}
});
// if (hasRange) {
const caretPos = selection.ranges.reduce((max: number, r: any) => Math.max(max, r.$to.pos), 0);
tr.setSelection(TextSelection.create(tr.doc, caretPos));
tr.scrollIntoView();
// }
dispatch(tr);
return true;
};
}
return pluginInfo;
}