Добавить улучшенную поддержку отмены при вставке форматированного текста

Сейчас в Discourse, если вы вставляете обычный текст в редактор, стандартное поведение браузера по нажатию Ctrl+Z отменяет вставку и удаляет вставленный текст. Однако, если вы вставляете форматированный текст, например этот жирный текст, отмена не работает. Аналогично, нельзя отменить вставку ссылок поверх текста для создания гиперссылок, а также вставку markdown-разметки через сочетания клавиш, такие как Ctrl+B, или при добавлении форматирования через кнопки панели инструментов редактора. В идеале, по возможности, все эти действия должны добавляться в стек отмены, чтобы Ctrl+Z работал корректно.

Позвольте описать ситуацию, которая со мной случается довольно часто:

  1. Я копирую текст с другого веб-сайта, который оказывается ссылкой, жирным шрифтом, заголовком и т.д. (Иногда я это замечаю, иногда — нет.)
  2. Я перехожу в Discourse, чтобы вставить этот текст в свой уже частично написанный пост.
  3. Нажимаю Ctrl+V, и вставляется форматированный текст.
  4. Я осознаю ошибку и нажимаю Ctrl+Z, но ничего не происходит.
  5. Я вручную удаляю вставленный по ошибке форматированный текст. (Чаще всего это ссылка, которую я вставил по ошибке, так что приходится удалять не просто символ # или что-то подобное.)
  6. Я нажимаю Ctrl+Shift+V, что следовало сделать сразу, чтобы вставить текст без форматирования.

Очевидно, что это частично ошибка пользователя, и когда я не ошибаюсь, процесс состоит всего из двух шагов (скопировать с другого сайта, вставить как обычный текст в Discourse). Но когда я всё же ошибаюсь (а это случается часто, так как я привык просто нажимать Ctrl+V, поскольку большинство сайтов не поддерживают вставку с форматированием), было бы здорово, если бы Ctrl+Z работал как обычно, позволяя сэкономить время.

4 лайка

Это, как правило, стандартное поведение браузера, и Discourse здесь ни при чём. Попробуйте протестировать это на обычном текстовом поле в HTML.

Правильно, по умолчанию отмена не работает, если вы используете JavaScript для изменения содержимого поля ввода. Однако я поискал в Google и обнаружил, что document.execCommand позволяет вставлять текст, добавляя изменения в стек отмены.

Например, если вы выполните document.getElementById('myInput').value = 'asd', а затем нажмете Ctrl+Z, отмена не сработает.

Однако, если вы используете document.execCommand('insertText', false, 'asd'), когда курсор находится в нужном месте (что должно быть так в соответствии с текущим рабочим процессом Discourse), текст будет корректно вставлен, и Ctrl+Z удалит добавленный текст, как и ожидалось.

По сути, я хочу узнать, можно ли использовать document.execCommand (или какой-то другой процесс, если будет найдено более подходящее решение) для добавления изменений в стек отмены, чтобы Ctrl+Z работал в таких случаях.

3 лайка

Нет — мы намеренно отказались от этой поддержки в Discourse несколько лет назад, отдав предпочтение тому, чтобы нативная обработка отмены текста в браузере работала должным образом на всех стандартных веб-сайтах.

Кажется, я что-то упускаю. Что именно означает «позволить браузеру обрабатывать отмену действий в нативном текстовом поле»? Насколько я понимаю, отмена действий для форматированного текста в Discourse вообще не работает. Вы хотите сказать, что неработающая отмена действий — это стандартное поведение для веб-сайтов?

Мое недоумение вызвано тем, что я не могу вспомнить ни одного веб-сайта (кроме таких приложений, как Microsoft Word, где отмена работает), который поддерживал бы вставку с сохранением форматирования, кроме Discourse. Поэтому мне не с чем сравнить Discourse, чтобы понять, что является «стандартом». Если бы вы могли указать мне несколько сайтов для сравнения, это было бы очень полезно.

2 лайка

Посмотрите

Нажмите Попробовать, затем введите текст в поле ввода, сделайте паузу, а затем нажмите Ctrl+Z, чтобы отменить свои действия. Вот демонстрация. Сначала мы нажимаем кнопку Попробовать, в результате чего в браузере отображается HTML-элемент <textarea>.

Я ввожу текст в поле ввода. Как видно на скриншоте, я ввёл:

Я ПРОСТО НАПИСАЛ ЭТОТ ТЕКСТ УРАААА!

Теперь, после ввода текста, я нажимаю Ctrl+Z, чтобы отменить ввод, и вижу следующее:

Обратите внимание, что текст вернулся в предыдущее состояние, и это было на 100% обработано самим браузером, без участия какого-либо JavaScript-кода.

Насколько я понимаю, @seanblue спрашивает, можем ли мы изменить используемые нами API браузера при работе с полем ввода текста, чтобы дать браузеру более точные подсказки и помочь ему лучше обрабатывать отмену действий. Это применилось бы только к сочетаниям клавиш, панели инструментов, загрузке файлов и подобным функциям.

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

Я не против экспериментов здесь; возможно, если сообщество захочет, они могут прислать нам PR, чтобы показать, как это можно сделать.

7 лайков

Мы определённо могли бы сделать это лучше. Многие кнопки нашей панели инструментов и поведение при вставке с форматированием напрямую устанавливают значение текстового поля через JavaScript. Это полностью ломает нативную историю отмены/повтора действий браузера.

Вместо этого, всякий раз, когда мы вносим программные изменения в текстовое поле, мы должны использовать document.execCommand (как упомянул @seanblue). Таким образом, браузер интерпретирует это как действие пользователя и аккуратно добавляет его в историю отмены/повтора.

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

10 лайков

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

Сначала откройте редактор Discourse:

Теперь скопируйте следующий текст и вставьте его в редактор: this is a test

Теперь нажмите ctrl+z, и вставленный текст удаляется. Это идентично поведению, которое вы продемонстрировали в своём посте.

Теперь скопируйте вместо этого следующий текст и вставьте его в редактор: this is a great test
Обратите внимание, что он вставляется с markdown-разметкой для курсива слова “great”.

Теперь нажмите ctrl+z и обратите внимание, что вставленный текст остаётся на месте. Вот о чём я говорил.


Верно. Я не предлагаю обрабатывать отмену самостоятельно на JavaScript. Я предлагаю, что при манипуляциях с текстовым полем вы сообщаете об этом браузеру, чтобы он сам мог отменить изменение, когда пользователь нажмёт ctrl+z.

Кстати, 99% моего разочарования было бы устранено, если бы ctrl+z работал после вставки форматированного текста. Было бы идеально, если бы каждое отдельное действие можно было отменить с помощью ctrl+z? Конечно. Но большинство других действий можно отменить, повторив исходное действие (например, ctrl+b может как добавить, так и убрать жирное начертание в markdown). Однако при вставке есть вероятность включения значительного объёма непредвиденной разметки, включая заголовки, ссылки и даже таблицы, поэтому крайне важно, чтобы отмена работала именно в этом случае.

Если мы сузим область до обработки отмены только в случае вставки форматированного текста и проигнорируем другие сочетания клавиш, кнопки панели инструментов и т. д., снизит ли это риск достаточно, чтобы попробовать это реализовать?

9 лайков

Понял; благодаря вашим объяснениям и объяснениям @david я теперь вижу разницу. Я просто редко использую эти кнопки в редакторе. Я ввожу текст в поле ввода с клавиатуры компьютера (физической или экранный), и браузер обрабатывает это без сбоев.

6 лайков

Это понятно. Я тоже чаще ввожу markdown вручную, чем использую кнопки панели инструментов, так что для меня этот аспект не является проблемой. Я упомянул панель инструментов в первом посте лишь для того, чтобы подчеркнуть, что такая ситуация возникает не только при вставке отформатированного текста. Возможность отмены действий панели инструментов не так уж важна, поскольку пользователь выполняет их намеренно. Однако при вставке форматирование часто бывает случайным и неожиданным, поэтому возможность его отмены была бы очень удобной.

6 лайков

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

3 лайка

Пока не запланировано, но да, похоже, это сработает. Я думаю, нам стоит изменить реализацию как для панели инструментов, так и для таких вещей, как сочетания клавиш вроде CTRL-B и упоминания.

Однако это довольно масштабное изменение; я бы сказал, что потребуется около 1–3 недель работы, чтобы всё это внедрить. Область воздействия обширна:

  • Вставка изображений через копирование и вставку
  • Загрузка файлов
  • Жирный / Курсив
  • Ссылки
  • Упоминания @
  • Автодополнение #

Я поддерживаю это изменение, но не уверен, когда сможем его запланировать… Думаю, я готов включить его в следующий релиз, есть ли возражения @codinghorror?

Мне нравится, что теперь можно будет использовать CTRL-Z, чтобы откатиться до пустого поля, а не просто упереться в первое упоминание, ссылку и т. д.

8 лайков

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

  • Вставка текста с форматированием, включая жирный шрифт, изображения и ссылки
  • Автодополнение упоминаний, категорий/тегов и эмодзи
  • Горячие клавиши, например Ctrl+B для жирного шрифта
  • Действия на панели инструментов, такие как жирный шрифт, скрытие деталей, размытие спойлеров и т. д.

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

8 лайков

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

8 лайков

Так как прошло уже четыре месяца, я хотел бы уточнить, как продвигается дело. :slight_smile:

2 лайка

Да, я вас прекрасно понимаю, но на это потребуется ещё немного времени.

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

Первым шагом, который снимет блокировку, станет поддержка редактора с атрибутом contenteditable, который будет выглядеть и работать так же, как наш текущий текстовый блок.

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

2 лайка

Без проблем, я просто хотел узнать о прогрессе.

Ого, я даже не слышал о contenteditable до сих пор. Не могли бы вы кратко объяснить техническую причину, по которой это изменение необходимо или желательно? Если нет — ничего страшного, я просто из любопытства.

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

Причина, по которой эта работа находится на критическом пути, заключается в том, что вся наша внутренняя архитектура сильно связана с конкретной реализацией (TEXTAREA). У нас нет единой функции для взаимодействия с редактором; вместо этого мы вынуждены копировать и вставлять около 20 различных реализаций.

Мы хотели бы создать небольшой «скелетный» компонент, который определял бы:

  • Как выделять текст
  • Как вставлять текст

и так далее. Тогда мы сможем переписать этот скелет как реализацию на основе contenteditable или как реализацию TEXTAREA, поддерживающую отмену действий.

Однако для этого потребуется переместить значительный объём кода.

2 лайка

Мне удалось немного продвинуться в этом вопросе:

Это далеко не так полно, как описанная @sam долгосрочная работа. Но, думаю, это поможет в краткосрочной перспективе. Теперь история отмен сохраняется при вставке форматированного текста, цитировании и использовании (большинства) кнопок редактора/горячих клавиш.

Это уже работает на Meta — дайте знать, если заметите какие-либо проблемы.

3 лайка