import { Extension } from '@tiptap/core' import { Plugin, PluginKey } from '@tiptap/pm/state' import type { EditorState } from '@tiptap/pm/state' import { Decoration, DecorationSet } from '@tiptap/pm/view' import { LATIN_CLASS, RTL_CLASS, findScriptRuns } from '../../utils/bidiText' /** نام مارک‌های قدیمی که خودشان اندازه/جهت متن را تعیین می‌کنند */ const LEGACY_MARKS = new Set(['englishText', 'smallSize']) type BidiState = { dir: 'rtl' | 'ltr' } /** کلید پلاگین — جهت متن با متای همین کلید به‌روز می‌شود */ export const bidiPluginKey = new PluginKey('bidiTypography') /** * تزئین دوزبانه متن ویرایشگر — اجرای لاتین (و در متن انگلیسی، اجرای فارسی) با یک * span جهت‌دار نشانه‌گذاری می‌شود تا ویرایشگر هم دقیقاً همان اندازه «۲ پیکسل کوچک‌تر» * خروجی سایت را نشان دهد. تزئین‌ها سند را تغییر نمی‌دهند و ذخیره نمی‌شوند. */ export const BidiTypography = Extension.create({ name: 'bidiTypography', addProseMirrorPlugins() { return [ new Plugin({ key: bidiPluginKey, state: { init: () => ({ dir: 'rtl' }), apply: (tr, value) => { const dir = tr.getMeta(bidiPluginKey) as BidiState['dir'] | undefined return dir && dir !== value.dir ? { dir } : value }, }, props: { decorations: (state) => buildDecorations(state, bidiPluginKey.getState(state)?.dir ?? 'rtl'), }, }), ] }, }) function buildDecorations(state: EditorState, dir: 'rtl' | 'ltr'): DecorationSet { const className = dir === 'rtl' ? LATIN_CLASS : RTL_CLASS const runDir = dir === 'rtl' ? 'ltr' : 'rtl' const decorations: Decoration[] = [] const { doc, schema } = state doc.descendants((node, pos, parent) => { if (node.type.name === 'codeBlock' || parent?.type.name === 'codeBlock') return false if (!node.isText || !node.text) return if (node.marks.some((mark) => mark.type === schema.marks.code || LEGACY_MARKS.has(mark.type.name))) { return } for (const run of findScriptRuns(node.text, dir)) { decorations.push( Decoration.inline(pos + run.from, pos + run.to, { class: className, dir: runDir }), ) } }) return DecorationSet.create(doc, decorations) }