`white-space` CSS 属性在富文本编辑器中粘贴时未被尊重

Priority/Severity:

Platform:

Operating System

  • Windows 11

Browser

  • Google Chrome 139.0.7258.128

Discourse

028c90dd5e7a2799ea5b6e963f71fc0222681943

Description:

从某些来源复制的文本除了纯文本(text/plain 类型)之外,还可能以格式化形式(text/html 类型)存储在剪贴板中。

当文本粘贴到编辑器时,如果剪贴板中存在格式化数据类型,则会使用该数据而不是纯文本类型。

默认情况下,HTML 内容中的空格会被折叠。此行为可以通过 white-space CSS 属性进行控制。

:bug:“富文本编辑器”模式下粘贴到编辑器时,剪贴板数据的 white-space CSS 属性未被尊重。这导致粘贴内容中的空格始终被折叠。在源内容设置了 white-space 属性为 pre 值的情况下,粘贴的内容难以阅读,并且在源内容的空格具有技术意义的情况下是不正确的。

Reproducible steps:

  1. 创建一个具有以下内容的 HTML 文件:
    <html>
      <body>
        <span style="white-space: pre">foo
    bar
        </span>
      </body>
    </html>
    
  2. 在您的网络浏览器中打开该文件。
    请注意,页面内容的空格未被折叠:
    foo
    bar
    
  3. 复制网页内容。
  4. 打开帖子编辑器。
  5. 将编辑器置于“富文本编辑器”模式。
  6. 粘贴复制的内容。

:bug: 粘贴内容的空格被折叠了,而不是像复制的内容那样具有相同的格式:

foo bar

Additional context:

我看到 ProseMirror 支持 white-space: pre


在使用“Markdown 编辑器”模式的编辑器时,不会出现此故障。


如果内容粘贴到代码块而不是普通编辑器模式,也不会出现此故障。确实,在许多情况下,将使用类似 white-space: pre 的内容放入代码块是最合适的方式。然而,用户通过将内容添加到编辑器,然后选择内容,再使用编辑器工具栏应用格式(而不是在添加内容之前触发代码块的替代方法)来追溯性地应用格式是相当常见的。


我发现这个工具对于检查剪贴板内容的原始数据很有用:


我可以在 try.discourse.org 的“安全模式”下重现此故障。

Related

2 个赞

您是在将网页内容粘贴到帖子编辑器中的“富文本编辑器”模式下操作的吗?

错误仍然存在。

您确定是严格按照说明操作的吗?

请注意,您必须复制 HTML 渲染后的内容,以便剪贴板中填充 text/html 类型的数据:

<html>
<body>
<!--StartFragment--><span>foo
bar
    </span><!--EndFragment-->
</body>
</html>

这不是关于使用 HTML 标记来撰写帖子。

啊,抓得好。我有点太快地浏览帖子了 :sweat_smile:

1 个赞

感谢你报告此问题 @per1234,我们正在查看。\n\n我们理解这里普遍存在的问题,我们希望让人们尽可能轻松地粘贴代码示例。

2 个赞

您期望从这样的 HTML 剪贴板中得到什么?

foo
bar

或者,考虑到它是一个 span 标签,两行内联代码之间有一个硬中断?

foo
bar

或者只是我们尊重换行符,但在常规段落中,之间有一个硬中断?

foo
bar

谢谢!

我对 HTML 主题不太了解,但我期望得到如下渲染:

据我所知,Chrome 浏览器就是这样渲染的。


话虽如此,但在我遇到问题的特定用法中,代码块渲染确实是最合适的。我们通过点击一个名为“Arduino Cloud Editor”的在线 IDE 中的“复制控制台输出”按钮来获取此类剪贴板内容:

这会将编译器和其他工具生成的输出复制到剪贴板。此类非散文内容最好格式化为代码块。

如果在论坛帖子中分享复制的输出时使用以下过程:

  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 个赞

现在不会了,这个问题最近已经修复(你可以在这里测试)。

在这种情况下,它是否仍然在其 text/html 剪贴板输出中使用 span 标签,还是只输出 plain/text

如果您使用“剪贴板检查器”工具在点击Arduino Cloud Editor中的“复制控制台输出”按钮后检查剪贴板中的数据,它会显示以下“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 的上下文中保留换行符,将它们转换为硬换行符。(感谢 Marijn 对 prosemirror-model 的改进:https://github.com/ProseMirror/prosemirror-model/commit/79e9f2b9497ec3aac70d180aa846267dafa48d9a)

随着代码工具栏按钮的最新改进,用户应该能够选择带有硬换行的粘贴部分并将其转换为代码块,并且换行符应该会被保留。

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,可从 Arduino 网站的“软件”页面下载:
    https://www.arduino.cc/en/software/#ide-download-section
  2. 启动 Arduino IDE。
  3. 在 Arduino IDE 菜单中选择 文件 > 新建草图
  4. 用以下代码替换新草图的内容:
    void setup() {
      Serial.begin(9600);
      while (!Serial) {}  // 等待串口端口打开。
      delay(500);         // 一些板子在串口端口初始化后需要延迟。
      Serial.println("foo");
      Serial.println("bar");
    }
    void loop() {}
    
  5. 在 Arduino IDE 菜单中选择 工具 > 串行监视器,如果尚未打开,则打开“串行监视器”视图。
  6. 在“串行监视器”视图的波特率菜单中选择“9600”。
  7. 将草图上传到您的 Arduino 板。
  8. 从“串行监视器”视图中的字段中选择串行输出。
  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 串行监视器错误地将复制内容的每一行包装在 <pre> 标签中,因此 Discourse 富文本编辑器将粘贴内容的每一行呈现为单独的代码块是正确的,也是符合预期的。

与我上面描述的其他问题一样,可以通过在粘贴内容之前主动触发代码块格式来避免意外的格式问题。

2 个赞