Свойство CSS `white-space` для данных буфера обмена не учитывается при вставке в WYSIWYG-редактор

Приоритет/Серьезность:

Средний

Платформа:

Операционная система

  • Windows 11

Браузер

  • Google Chrome 139.0.7258.128

Discourse

028c90dd5e7a2799ea5b6e963f71fc0222681943

Описание:

Текст, скопированный из некоторых источников, может храниться в буфере обмена в отформатированном виде (тип text/html) в дополнение к обычному тексту (тип text/plain).

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

По умолчанию пробелы в HTML-контенте сжимаются. Это поведение можно контролировать с помощью CSS-свойства white-space.

:bug: При вставке в редактор в режиме «редактор с форматированием» свойство white-space CSS для данных из буфера обмена не учитывается. В результате пробелы всегда сжимаются в вставленном содержимом. В случаях, когда исходный контент имел свойство white-space, установленное в значение pre, это приводит к тому, что вставленный текст становится трудно читаемым и некорректным в ситуациях, когда пробелы в исходном контенте имели техническое значение.

Шаги для воспроизведения:

  1. Создайте HTML-файл со следующим содержимым:
    <html>
      <body>
        <span style="white-space: pre">foo
    bar
        </span>
      </body>
    </html>
    
  2. Откройте файл в вашем веб-браузере.
    Обратите внимание, что пробелы в содержимом страницы не сжимаются:
    foo
    bar
    
  3. Скопируйте содержимое веб-страницы.
  4. Откройте редактор публикации.
  5. Переключите редактор в режим «редактор с форматированием».
  6. Вставьте скопированное содержимое.

:bug: Вместо сохранения того же формата, что и у скопированного содержимого, пробелы вставленного текста были сжаты:

foo bar

Дополнительная информация:

Я вижу, что ProseMirror поддерживает white-space: pre:


Ошибка не возникает при использовании редактора в режиме «редактор Markdown».


Ошибка не возникает, если содержимое вставляется в блок кода вместо обычного режима редактора. Действительно, во многих случаях наиболее уместно размещать контент, использующий что-то вроде white-space: pre, внутри блока кода. Однако довольно распространена ситуация, когда пользователи применяют форматирование постфактум: добавляют контент в редактор, выделяют его, а затем используют панель инструментов редактора для применения форматирования (в отличие от альтернативного подхода — создания блока кода до добавления контента).


Я обнаружил полезный инструмент для просмотра необработанных данных содержимого буфера обмена:


Мне удалось воспроизвести ошибку на try.discourse.org в режиме «безопасный режим».

Связанные темы

2 лайка

Вы переключили редактор сообщений в режим «редактор форматированного текста» перед вставкой содержимого, скопированного с веб-страницы?

Ошибка всё ещё возникает.

Вы уверены, что следовали инструкциям в точности так, как написано?

Обратите внимание: необходимо копировать содержимое, которое рендерится из этого HTML, чтобы в буфер обмена записались данные типа text/html:

<html>
<body>
<!--StartFragment--><span style="color: rgb(0, 0, 0); font-family: &quot;Times New Roman&quot;; font-size: medium; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: pre; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">foo
bar
    </span><!--EndFragment-->
</body>
</html>

Речь не идёт о составлении вашего сообщения с использованием HTML-разметки.

А, хорошо подметил. У меня есть привычка бегло просматривать посты немного слишком быстро :sweat_smile:

1 лайк

Спасибо за сообщение, @per1234, мы разбираемся с этим.

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

2 лайка

Чего бы вы ожидали от такой HTML-буферной области?

foo
bar

Или, учитывая, что это тег span, две строки внутристрочного кода с жестким разрывом между ними?

foo
bar

Или просто то, что мы уважаем переносы строк, но в обычном абзаце, с жестким разрывом между ними?

foo
bar

Спасибо!

Я не очень разбираюсь в HTML, но ожидал бы такого отображения:

Насколько я могу судить, именно так это отображает браузер Chrome.


Тем не менее, в конкретном случае, с которым я столкнулся, верным будет отображение в виде блока кода. Такой контент буфера обмена мы получаем, нажав кнопку «Копировать вывод консоли» в онлайн-IDE под названием «Arduino Cloud Editor»:

Это копирует вывод, сгенерированный компилятором и другими инструментами, в буфер обмена. Такой тип контента, не являющегося обычным текстом, лучше всего форматировать как блок кода.

Если для размещения скопированного вывода в сообщении форума используется следующая процедура:

  1. Переключить редактор сообщения в режим «богатого текста».
  2. Вставить содержимое в редактор.
  3. Выделить вставленное содержимое.
  4. Нажать иконку </> на панели инструментов редактора.

Сообщение в итоге получит следующее форматирование:

/run/arduino/sketches/asdf/asdf.ino:1:2: error: #error foo  #error foo   ^~~~~

(обратите внимание, что весь скопированный текст находится на одной строке)

в то время как мы ожидаем такое форматирование сообщения:

/run/arduino/sketches/asdf/asdf.ino:1:2: error: #error foo
 #error foo
  ^~~~~

Однако это предпочтение в пользу блока кода специфично для нашего конкретного случая. Возможно, в других сценариях существуют источники контента буфера обмена со свойством white-space: pre, для которых блок кода не будет уместен. И даже в нашем случае разумно возложить на пользователя ответственность за ручное применение форматирования блока кода.

1 лайк

В данном случае используется ли тег span в выводе буфера обмена text/html или выводится только plain/text?

Если я использую инструмент «Clipboard Inspector» для проверки данных в буфере обмена после нажатия кнопки «Copy Console Output» в редакторе Arduino Cloud, он показывает, что буфер содержит следующие данные типа “text/plain”:

/run/arduino/sketches/asdf/asdf.ino:1:2: error: #error foo
 #error foo
  ^~~~~

а также следующие данные типа “text/html”:

<span style="color: rgb(0, 0, 0); font-family: &quot;Open Sans&quot;, &quot;Lucida Grande&quot;, lucida, verdana, sans-serif; font-size: 16px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: 0.16px; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: pre; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none;">/run/arduino/sketches/asdf/asdf.ino:1:2: error: #error foo
 #error foo
  ^~~~~</span>

Надеюсь, это отвечает на ваш вопрос. Я с радостью предоставлю дополнительную информацию, если потребуется.

Это должно быть исправлено в FIX: [rich editor] convert newlines to hard breaks when parsed from HTML by renato · Pull Request #35518 · discourse/discourse · GitHub (еще не слито, ожидает код-ревью).

Моя первая попытка заключалась в преобразовании этого в блок кода, но я думаю, что это было бы слишком поспешно и могло бы привести к ложным срабатываниям. Вместо этого мы просто уважаем переносы строк, преобразуя их в жесткие переносы в контексте, где был вставлен HTML. (Спасибо Марию за улучшение prosemirror-model: When preserving whitespace, replace newlines with line break replacem… · ProseMirror/prosemirror-model@79e9f2b · GitHub)

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

2 лайка

Огромное спасибо за исправление, @renato, и за то, что нашли время опубликовать обновление здесь!

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


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

Искажение из-за случайного синтаксиса разметки

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

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

Неверное форматирование из-за некорректной разметки в содержимом буфера обмена

У нас есть случай, когда содержимое типа “text/html”, добавляемое в буфер обмена при копировании из определённого приложения, содержит некорректную HTML-разметку, что приводит к неверному форматированию при вставке содержимого в текстовый редактор с поддержкой форматирования вне блока кода.

Это, разумеется, ошибка в приложении, и Discourse работает на 100% правильно, форматируя содержимое в соответствии с указанной разметкой.

1 лайк

Огромное спасибо, @per1234!

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

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

Конечно. Я рад, если эта информация окажется полезной. Позвольте мне повторить своё предыдущее утверждение:

Однако я не против ошибаться насчёт этого :slightly_smiling_face:.

  1. Скопируйте следующий код на C++:
    #include <iostream>
    int main() {
      std::cout << __FILE__;
    }
    
  2. Откройте редактор сообщения.
  3. Переключите редактор в режим «богатого текста».
  4. Вставьте скопированный контент в редактор.

:slightly_frowning_face: Контент повреждён:

#include
int main() {
std::cout << FILE;
}

(обратите внимание, что <iostream> был удалён, так как был воспринят как неподдерживаемый HTML-тег, а __FILE__ был обработан как жирный шрифт)

Это можно считать ошибкой пользователя, поскольку её можно избежать, предварительно активировав блок кода перед вставкой контента, не являющегося обычным текстом. Однако мы могли бы ожидать, что альтернативный рабочий процесс — применение форматирования блока кода постфактум к вставленному контенту — будет столь же допустимым (как это работает при использовании редактора Markdown).

Оборудование

  • Любая плата Arduino (как официальная, так и сторонняя)

Инструкция

  1. Установите Arduino IDE 2.3.6, которую можно загрузить со страницы «Software» на сайте Arduino:
    https://www.arduino.cc/en/software/#ide-download-section
  2. Запустите Arduino IDE.
  3. В меню Arduino IDE выберите File > New Sketch.
  4. Замените содержимое нового скетча следующим кодом:
    void setup() {
      Serial.begin(9600);
      while (!Serial) {}  // Ждём открытия последовательного порта.
      delay(500);         // Некоторым платам требуется задержка после инициализации последовательного порта.
      Serial.println("foo");
      Serial.println("bar");
    }
    void loop() {}
    
  5. В меню Arduino IDE выберите Tools > Serial Monitor, чтобы открыть окно Serial Monitor, если оно ещё не открыто.
  6. В окне Serial Monitor выберите «9600» в меню скорости передачи данных (baud rate).
  7. Загрузите скетч на вашу плату Arduino.
  8. Выделите вывод в поле окна Serial Monitor.
  9. Скопируйте выделенный контент.
  10. Откройте редактор сообщения в Discourse.
  11. Переключите редактор в режим «богатого текста».
  12. Вставьте скопированный контент в редактор.

:slightly_frowning_face: Каждая строка скопированного контента помещается в отдельный блок кода:

foo

bar

Если вы проверите содержимое буфера обмена, то увидите, что помимо ожидаемого контента типа text/plain:

foo
bar

оно также содержит следующий контент типа text/html:

<div style="color: rgb(78, 91, 97); font-family: monospace; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: nowrap; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; position: absolute; left: 0px; top: 0px; height: 18px; width: 1862px;"><pre style="margin: 0px;">foo
</pre></div><div style="color: rgb(78, 91, 97); font-family: monospace; font-size: 13px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: nowrap; background-color: rgb(255, 255, 255); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; position: absolute; left: 0px; top: 18px; height: 18px; width: 1862px;"><pre style="margin: 0px;">bar</pre></div>

Поскольку монитор последовательного порта Arduino IDE 2.x ошибочно оборачивает каждую строку скопированного контента типа text/html в теги <pre>, отображение каждой строки вставленного контента как отдельного блока кода в редакторе богатого текста Discourse является правильным и ожидаемым.

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

2 лайка

Мы парсим вставленный plain/text как Markdown, что ожидаемо, и отказ от этого, на мой взгляд, ухудшил бы пользовательский опыт. Тем не менее, любые практические предложения приветствуются. Возможно, поддержка модификатора SHIFT для функции «вставить без парсинга как Markdown» могла бы помочь?

Это можно изменить. Один из возможных вариантов — экранировать его как \<iostream\> вместо удаления.


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

Есть ли ещё что-то, с чем вы сталкиваетесь в рамках этой темы?

Эта тема была автоматически закрыта через 64 дня. Новые ответы больше не принимаются.