Обновление Mathjax до версии 4

@sam и всем, кто интересуется набором математики в Discourse. Я обновил плагин discourse-math, чтобы он использовал MathJax V3 вместо значительно более медленной и устаревшей V2. Как и ожидалось, результат — гораздо более отзывчивый пользовательский опыт при сохранении богатого функционала по сравнению с KaTeX.

Я с радостью подготовлю pull request, если вам понравятся результаты.


Вы можете увидеть это в действии на моем образовательном сайте Discourse:

Большая часть контента на этом сайте закрыта или не включена в индекс. Однако вверху в категории MathJax V3 должно быть несколько тем, иллюстрирующих основные идеи.

Вы можете изучить код плагина в этом отдельном репозитории discourse-mathjax. Файл, в который внесено наибольшее количество изменений, — инициализатор.

Вы также можете использовать этот репозиторий для установки плагина на отдельный сайт прямо сейчас. Просто не забудьте удалить старый репозиторий во время установки. Таким образом, вам нужно будет изменить стандартную технику установки плагинов следующим образом:

hooks:
  after_code:
    - exec:
        cd: $home/plugins
        cmd:
          - rm -r discourse-math
          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/mcmcclur/discourse-math.git

Комментарии

Последняя версия MathJax на самом деле 4.0.0. Я выбрал V3.2.2 по нескольким причинам:

  • Хотя V4 значительно быстрее V2, она не столь быстра, как V3.
  • Пользовательский опыт в V4 немного отличается, особенно если пользователь кликает на вывод.
  • Статус 4.0.0 заставляет задуматься о количестве возможных ошибок.

Тем не менее, API для V4 идентичен API V3. Вероятно, позже можно будет обновиться, просто добавив последний репозиторий MathJax.

Мне пришлось внести одно небольшое изменение в файл locales/server.en.yml. Конечно, существует множество подобных файлов для разных языков. Насколько я понимаю, эти файлы будут автоматически переведены позже?

Я вообще не использую чат и не тестировал его в этом контексте.

Пулл-реквест на обновление MathJax до версии 3, все тесты пройдены!

Касательно:

Это замечательно :hugs:, но я думаю, не стоит ли воспользоваться этим случаем, чтобы немного сократить наш репозиторий.

Теперь, когда MathJax перенесён в ядро, мы можем полагаться на pnpm для загрузки пакета и избежать упаковки всего исходного кода, как это делается, например, для FullCalendar.

В частности, наша цель — хранить в репозитории только «ссылки», а процесс сборки будет подтягивать необходимые зависимости.

Дайте нам пару дней, я хочу обсудить это с командой разработчиков с опытом работы в этой области. Большое спасибо за ваши усилия!

Да, я считаю, что это определённо правильное решение. Я всегда немного удивлялся, зачем вы упаковываете всё целиком!

Значит, я полагаю, вы создадите функцию loadMathJax для вашей библиотеки, которая будет использоваться для загрузки MathJax?

Хочу отметить, что включение всех плагинов в ядро сделало работу с ними немного сложнее. Привязка зависимостей к процессу сборки только усложнит ситуацию, хотя я уверен, что мог бы загрузить MathJax или FullCalendar с CDN.

В основном я говорю о случаях, когда я экспериментирую с плагинами для использования в своих собственных форумах, но я абсолютно уверен, что вам следует подгружать MathJax во время сборки.

Конечно! Я использую Discourse уже много лет и очень рад, что вам это нравится! :rocket:

Да, именно. Хорошим примером для копирования является morphlex:

Не знаю, удалось ли вам уже обсудить это с вашей командой по разработке опыта? Я с радостью помогу, если смогу. Хотя, по моим ощущениям, без вашей обратной связи я ничего не смогу сделать.

Я внес несколько дополнительных изменений в отдельную ветку, о которых скоро напишу. Я понимаю, что у вас сейчас очень много дел, поэтому не хочу быть навязчивым!

Я модифицировал плагин discourse-math, чтобы он мог обрабатывать гораздо больше типов математического ввода.

@sam Когда я впервые внес вклад в этот плагин в 2017 году, я помню, что вы были очень категоричны в желании строгого парсинга. Сразу скажу, что моя основная мотивация ослабить и расширить парсинг заключалась в том, чтобы он лучше работал с ИИ. В частности, когда вы общаетесь о математике с ботом на базе ИИ, вы часто обнаруживаете, что он отвечает, используя LaTeX, и существует множество способов, которыми он может выбрать для ограничения этого ввода в формате LaTeX. Поэтому, хотя я понимаю вашу мотивацию для строгого парсинга, изменения, которые я внес, являются скорее необходимыми для этого варианта использования.

Конечно, вам может быть не важен этот вариант использования, поэтому я поместил изменения в отдельную ветку, отличную от моего pull-запроса V3. Если вы решите, что они вам нравятся, я с радостью отправлю новый pull-запрос.

Конкретные изменения в pull-запросе:

Он принимает встроенную математику, ограниченную скобками со слешем, например \(a^2+b^2=c^2\).

Он принимает отображаемую математику в одну строку, ограниченную двойными долларами, например
$$a^2+b^2=c^2.$$

Он принимает отображаемую математику в одну строку, ограниченную скобками со слешем, например 
\[a^2+b^2=c^2.\]

Он принимает отображаемую математику в несколько строк, ограниченную скобками со слешем, например 
\[
a^2+b^2=c^2.
\]

Конечно, он по-прежнему принимает вводы из оригинала:

Встроенная математика, ограниченная долларами: $a^2+b^2=c^2$.

Отображаемая математика в несколько строк, ограниченная двойными долларами:
$$
a^2+b^2=c^2.
$$

Вы можете найти соответствующую ветку здесь.

Код также существует в виде отдельного плагина.

О, вы также можете увидеть его в действии!

@mcmcclur Спасибо за вашу работу. Было бы здорово увидеть эти функции в ядре.

Огромное спасибо, Марк.

Моя главная проблема здесь в том, что я очень хочу перейти на новые паттерны распределения зависимостей, см.:

Можешь ли ты посмотреть на это?

Что касается расслабленного синтаксиса, мне кажется, это должно быть настройкой сайта, возможно, даже включённой по умолчанию, учитывая все доступные LLM?

@mcmcclur Я сегодня немного поигрался с этим:

До готовности ещё далеко… но что-то вроде как запускается с версией 4.1, что приятно.

Да, это определённо прогресс!

Первая ключевая проблема, которую нужно решить, как я полагаю, вам известна: шрифты не находятся. На самом деле, я поэкспериментировал с этой строкой в discourse-math-mathjax.js:

fontURL: getURLWithCDN("/assets/mathjax/woff-v2"),

В качестве теста я указал URL просто на временную директорию на своём веб-сервере, и начальные результаты выглядят очень хорошо. Значит, дело в правильной установке этих шрифтов в Discourse.

В простом проекте pnpm на моём компьютере следующая команда устанавливает шрифты:

pnpm install @mathjax/mathjax-newcm-font@4

Когда я запускаю эту команду внутри discourse/frontend/discourse, шрифты появляются в

/discourse/frontend/discourse/npm_modules/@mathjax/mathjax-newcm-font/chtml/woff2/

Однако после сборки эти шрифты, похоже, не попадают в /assets/mathjax/woff-v2. Я пробовал множество вариантов с директорией, но ничего не сработало. Предполагаю, что это какая-то магия маршрутизации, в которой я не эксперт. Я почти уверен, что смогу добиться значительного прогресса в её устранении, как только проблема с путём будет решена.

@sam Я думаю, что добился значительного прогресса в этом вопросе, но есть одно важное ограничение. Я не уверен, откуда нужно загружать требуемые компоненты. В терминах кода это выглядит так:

window.MathJax = {
    loader: {
      // Это не работает:
      // paths: { mathjax: getURLWithCDN("/assets/mathjax") },
      // А это работает отлично:
      paths: { mathjax: "https://cdn.jsdelivr.net/npm/mathjax@4.1.0" },
      load: ["core", "input/tex", "input/mml", "output/chtml", "output/svg"],
    },
    // Дальнейшая конфигурация ...
  };

Когда я говорю, что закомментированная версия не работает, я имею в виду, что получаю явное сообщение:
MathJax(core): Can’t load “/assets/mathjax/core.js”

Обратите внимание, что в обоих случаях функция loadMathJax загружает запуск MathJax из локальной копии. То есть у меня есть следующее в файле:
/discourse/frontend/discourse/app/static/mathjax-bundle.js

export * from "mathjax/startup.js";

Затем loadMathJax, определённая в
/discourse/frontend/discourse/app/lib/load-mathjax.js,
вызывает:

const bundle = await import("discourse/static/mathjax-bundle");

Это предполагает несколько возможностей:

  1. Возможно, /assets/mathjax — это не правильное расположение, или
  2. Возможно, эти ресурсы нужно каким-то образом зарегистрировать, чтобы они появились в сборке (dist)?

Работая с версией CDN, я могу добиться значительного прогресса, но полагаю, что для вас это является серьёзным препятствием.

Я могу поделиться с вами своим кодом, если хотите, но, возможно, этой информации уже достаточно для постановки диагноза?

Конечно, код будет очень полезен здесь. Возможно, сделайте форк репозитория Discourse, затем загрузите свои изменения в ветку, и я смогу подтянуть изменения из вашей ветки в ваш pull request.

Очень рад, что вы продвигаетесь в диагностике этой проблемы.

Можете ли вы также обновить до последней версии? Я провёл цикл очистки.

Хорошо, вот код:

https://github.com/mcmcclur/discourse/tree/mathjax-mcmcclur

Однако будьте осторожны: я работал не напрямую с вашим последним коммитом. Я начал с основной ветки Discourse и вносил изменения оттуда. Таким образом, я многому научился на основе вашей работы, но общая структура отличается.

Я думаю, что основную разницу можно сформулировать так: там, где вы (естественным образом) используете функции Discourse, унаследованные от Ember, для координации таймингов, связанных с такими процессами, как загрузка и типографирование, я использую возможности MathJax. Поэтому мои пакеты load-mathjax и mathjax (один для SVG, другой для chtml) намного проще ваших. Загрузка координируется через объект window.MathJax в discourse-math-mathjax.

У меня всё ещё сохраняется та же проблема, которую я описывал ранее: именно этот закомментированный загрузчик не работает; вместо него мне приходится использовать версию из CDN. Я действительно не знаю почему.

Кажется, что ваш код страдает от той же проблемы. Вот почему AsciiMath, похоже, не работает.

Можете перепроверить мой последний коммит? Я добавил фильтр для Ember, так что теперь сборка Ember размещает все файлы в нужном месте.

Хорошо, у меня есть отличные новости и немного расстраивающие.

Во-первых, вы абсолютно правы: добавление воронки размещает эти файлы в правильном месте. Я добавил воронку в свою ветку, и теперь всё работает отлично без зависимости от CDN. :tada:

К сожалению, я не могу запустить ваш код в данный момент. Всякий раз, когда я перехожу на страницу с математикой, математика не отображается, и в консоли я вижу следующее сообщение об ошибке:
Uncaught (in promise) Error: State EXPLORER already exists

Я уверен, что раньше ваш код работал, так что, вероятно, это что-то сделал я. Для ясности: я буквально создал совершенно новую директорию, используя методы, описанные в Установка Discourse на macOS для разработки.

git clone https://github.com/discourse/discourse.git ./discourse
cd ./discourse
bundle install
pnpm install
bundle exec rake db:create
bundle exec rake db:migrate
RAILS_ENV=test bundle exec rake db:create db:migrate

# В одном терминале
bundle exec rails server

# В другом терминале
bin/ember-cli

Затем я загрузил ваш код с помощью:

git checkout 71ad0305f812311f2a4570edf7c33f97de46c457
git switch -c mathjax-sam

Даже из этой свежей настройки я получаю ошибку.


На данный момент я довольно доволен своей версией кода, но всё ещё любопытно, что происходит у вас. Однако мне нужно сделать перерыв в связи с праздниками. Я с радостью вернусь к этому через несколько дней.

Одна последняя мысль: насколько мне известно,

await import("tex-mml-chtml.js") // за которым следует
await import("input/asciimath.js")

не должно работать, что, как я думаю, фактически делает ваш код.

Я неточно указываю пути, но моя суть в том, что я не уверен, что последовательные динамические вызовы import приводят к правильной структуре MathJax. Мне кажется, что загрузка компонентов MathJax довольно сложна, и именно поэтому у них такой детальный процесс загрузки с объектом MathJax и всем остальным.

Огромное спасибо за вашу помощь и терпение, @sam!

Прогресс уже есть:

Я перенёс огромные JavaScript-пакеты в отдельный gem:

Это значительно упростит поддержку актуальности, к тому же MathJax больше не будет храниться в репозитории.

Привет, Сэм! Сегодня я много экспериментировал с этим. Выглядит отлично! Однако я считаю, что ещё предстоит сделать многое. Часть задач я точно могу помочь решить, но некоторые, возможно, окажутся мне не по плечу, особенно с учётом того, что университет снова начал работу.

В любом случае, вот несколько моих мыслей.

Масштабирование

В MathJax V4 масштабирование при наведении больше недоступно. Однако его легко настроить для масштабирования по Alt-клику. Я уже сделал это здесь:

Обратите внимание, что существует известная ошибка в MathJax, которую нужно исправить с помощью небольшого CSS-кода, как описано в этой проблеме на GitHub. Я также включил это исправление в данный код.

Параметры загрузки

В текущем виде AsciiMath нельзя включить, а функцию доступности — отключить. Я думаю, это связано с тем, как подмодули загружаются последовательно в load-mathjax.js.

Как я уже упоминал в своём предыдущем сообщении, гораздо чаще заранее определяют объект window.MathJax, указывая нужные компоненты. Объект MathJax переопределяется при загрузке основного скрипта. Именно так мне удалось заставить это работать в моей версии для V3. Если хочешь, я мог бы внедрить этот подход в твой код в начале следующей недели?

Когда мы разберёмся с параметрами, возможно, стоит также рассмотреть, есть ли в V4 новые опции, которые стоит добавить.

Визуальный редактор

Это просто замечательно — я очень рад это видеть!

Интересно, можно ли добавить в модальное окно контекстное меню с эффектным AI-помощником? Я спрашиваю, потому что студенты (и даже профессора :confused:) иногда испытывают трудности с вводом LaTeX. Небольшой AI-редактор может значительно упростить этот процесс. Я уже внедрил его в свой учебный Discourse и с нетерпением жду возможности использовать в предстоящем семестре.


Ладно, уверен, что есть ещё много чего, но на сегодня я почти закончил.

Огромное спасибо!!! :rocket: :fire: :tada:

Я понимаю, что плагин discourse-math полагается на отдельный gem с ресурсами MathJax/KaTeX, а не включает эти библиотеки напрямую, что делает плагин легковесным и позволяет обновлять математические библиотеки независимо.

Я хотел бы помочь проверить это перед первым релизом в продакшене, проведя тестирование в реальных условиях. Моя первоначальная идея заключалась в запуске отдельного временного экземпляра и включении там плагина для проверки контента с обилием математики, загрузки ресурсов через стандартный конвейер, поведения CSP и производительности.

Прежде чем приступить, я хотел бы уточнить, какая среда рекомендуется на данном этапе — уместно ли раннее тестирование в окружении, похожем на продакшен, или вы предпочитаете, чтобы это делалось в среде разработки до первого релиза в продакшене.

Я с радостью проведу тестирование любым способом, который будет наиболее полезен, и сообщу о любых проблемах или пограничных случаях, с которыми столкнусь, в соответствующий репозиторий. Из-за учёбы в университете я не могу обязаться соблюдать фиксированный график тестирования, но готов проводить тестирование по мере возможности и, скорее всего, у меня будет значительно больше свободного времени после 6 июня.

Теперь опции работают корректно; код можно посмотреть здесь:

Вот несколько комментариев:

  • Вся конфигурация и загрузка обрабатываются объектом MathJaxInitConfig, определённым в math-renderer.js.
  • Я удалил довольно много неиспользуемого кода из load-mathjax.js.
  • Расширение ‘ui/safe’ загружается всегда.
  • Я добавил опцию «Discourse math enable menu» (включить меню математического форматирования в Discourse), которая по умолчанию имеет значение true. Если установить false, меню полностью убирается, что ещё больше ускоряет работу MathJax.
  • Следующие два пункта меню:
    • Discourse math zoom on click (увеличение при клике)
    • Discourse math enable accessibility (включить доступность)
      Они не действуют, если меню отключено, но при включенном меню работают независимо друг от друга.

Всё меню выглядит так:

Пока я не добавил тесты, но могу попробовать, если вы хотите получить pull request.