Давным-давно я написал typofilter.js: небольшой скрипт, который расставляет «ёлочки», длинные тире, многоточия, © и прочие стрелочки. На прошлой версии домовой страницы он жил в Markdown-редакторе OctoberCMS, также я дружил плагин со старыми версиями CKE и tinyMCE, и даже делал плагин для обсидиана.
Сейчас, конечно, любители типографически корректных текстов оказались под угрозой. В авторах текстов с длинными тире и кавычками ёлочкой любой обыватель немедленно распознаёт генеративную нейросеть. Однако что делать, если вы ставили тирешечки и кавычечки задолго до того, как первая нейросеть распознала котика на картинке? Что делать, если вы прикипели к типографике? Или наоборот, хотите незаметно слиться с роботами, стать неотличимым от них, чтобы вас нечаянно не заменили?
В общем, лично я отказываться от типографики не собираюсь. Так что перетащил плагин в новую версию сайта и подружил с Drupal и CKEditor 5. Заодно и некоторые проблемки поправил.
Что починили в typofilter.js
Библиотека используется не только здесь. Поэтому сначала мы зафиксировали её текущее поведение: собрали с клодом полсотни примеров (простой текст, Markdown, HTML), записали, что она выдаёт, и превратили это в тесты на встроенном node:test. Дальше каждую правку сверяли с этим набором: меняться должно только то, что чиним. Плюс добавили проверку на идемпотентность: повторный прогон по уже обработанному тексту ничего не меняет, так что жать кнопку можно сколько угодно.
В процессе исправлено несколько багов, связанных с маркдауном, вложенными кавычками и защищенными (от типографики) блоками.
Плагин для CKEditor 5
Плагин для CKEditor 4 пришлось выкинуть целиком. API поменялся, да и логика устарела (он даже теги не поддерживал).
Новый плагин добавляет кнопку на панель, команду typofilter и горячую клавишу Ctrl+Shift+T. Если текст выделен, обрабатывается только выделение, если нет — весь текст.
Плагин — обычный JS без сборки. Главная тонкость была в том, откуда брать классы CKEditor: в Drupal редактор собран как DLL (CKEditor5.dll(…)), в браузерной сборке с CDN всё лежит в глобальном CKEDITOR, а при сборке через npm классы импортируются из пакета ckeditor5. Поэтому плагин создаётся фабрикой из классов того редактора, который есть на странице, и сам находит DLL- или UMD-сборку.
Проверили все три варианта на текущем CKEditor 5 v49 и на версии, которая идёт с Drupal 11.
Как поставить на сайт с CKEditor 5
Самый простой вариант — браузерная (UMD) сборка CKEditor. Подключаем после неё typofilter.js и плагин, а в конфиге добавляем Typofilter в плагины и typofilter в тулбар:
<link rel="stylesheet" href="/ckeditor5/ckeditor5.css">
<script src="/ckeditor5/ckeditor5.umd.js"></script>
<script src="/typofilter/typofilter.js"></script>
<script src="/typofilter/plugins/ckeditor5/typofilter.js"></script>
<script>
const { ClassicEditor, Essentials, Paragraph, Bold, Link } = CKEDITOR;
ClassicEditor.create(document.querySelector('#editor'), {
licenseKey: 'GPL',
plugins: [Essentials, Paragraph, Bold, Link, Typofilter],
toolbar: ['bold', 'link', '|', 'typofilter'],
typofilter: { label: 'Типографика', keystroke: 'Ctrl+Shift+T' },
});
</script>
Файлы CKEditor берутся из npm-пакета ckeditor5 (dist/browser/) или с их CDN. licenseKey: 'GPL' нужен начиная с CKEditor 44, без него редактор не запустится.
Если редактор собирается через npm и сборщик, библиотеку можно поставить прямо с GitHub (npm install github:graker/typofilter.js) и собрать плагин из классов пакета ckeditor5:
import { ClassicEditor, Essentials, Paragraph, Plugin, Command, ButtonView } from 'ckeditor5';
import 'typofilter.js/typofilter.js';
import 'typofilter.js/plugins/ckeditor5/typofilter.js';
const Typofilter = window.createTypofilterPlugin({ Plugin, Command, ButtonView });
ClassicEditor.create(document.querySelector('#editor'), {
licenseKey: 'GPL',
plugins: [Essentials, Paragraph, Typofilter],
toolbar: ['typofilter'],
});
Как поставить на Drupal
У библиотеки теперь есть composer.json с типом drupal-library, так что в стандартном Drupal-проекте (drupal/recommended-project) composer положит её в web/libraries/typofilter. Пакета на Packagist нет, поэтому добавляем репозиторий:
composer config repositories.typofilter vcs https://github.com/graker/typofilter.js
composer require graker/typofilter:^1.2
Плагины CKEditor 5 Drupal ищет только в модулях, поэтому понадобится свой модуль (или любой уже существующий кастомный). Пусть он называется mymodule. Сначала описываем библиотеки в mymodule.libraries.yml:
typofilter:
js:
/libraries/typofilter/typofilter.js: {}
ckeditor5.typofilter:
js:
/libraries/typofilter/plugins/ckeditor5/typofilter.js: {}
dependencies:
- core/ckeditor5
- mymodule/typofilter
version у библиотеки лучше не указывать: Drupal подставит его в URL файла (?v=1.2.0), и после обновления библиотеки браузеры будут держать старый файл, пока не поднимешь версию руками. Без неё URL меняется при каждом сбросе кэша.
Дальше регистрируем плагин в mymodule.ckeditor5.yml:
mymodule_typofilter:
ckeditor5:
plugins:
- typofilter.Typofilter
drupal:
label: Typography
library: mymodule/ckeditor5.typofilter
toolbar_items:
typofilter:
label: Typography
elements: false
Сбрасываем кэш (drush cr), идём в «Конфигурация → Создание содержимого → Форматы текста и редакторы», открываем нужные форматы и перетаскиваем кнопку «Typography» в тулбар.
Пара необязательных мелочей. Чтобы на экране настройки тулбара у кнопки была иконка, а не пустой квадрат, нужна admin-библиотека с CSS для класса .ckeditor5-toolbar-button-typofilter, а SVG лежит в библиотеке: plugins/ckeditor5/typofilter.svg. А чтобы подпись кнопки переводилась, можно указать в drupal.class свой класс плагина (наследник CKEditor5PluginDefault) и отдавать из getDynamicPluginConfig() конфиг вида ['typofilter' => ['label' => (string) $this->t('Typography')]].
Код и тесты — на GitHub, пользуйтесь на здоровье. Пишите, если какие вопросы.