Add better undo support when inserting formatted text

Right now in Discourse, if you paste plain text into the composer, typical browser behavior via Ctrl+Z will undo that paste and remove the pasted text. However, if you paste formatted text such as this bold text, undo doesn’t work. Similarly, pasting links on top of text to linkify them cannot be undone, nor can the markdown inserted via shortcuts like Ctrl+B or when adding formatting from the composer toolbar options. Ideally, as much of this as possible would be added to the undo stack so Ctrl+Z works.

Let me give a description of what happens to me fairly often.

  1. I copy some text from another website which happens to be a link, bold, a header, etc. (Sometimes this fact registers and sometimes it does not.)
  2. I go to Discourse to paste this text into my already partially written post.
  3. I hit Ctrl+V which inserts the formatted text.
  4. I realize my mistake and hit Ctrl+Z, which does nothing.
  5. I manually remove the formatted text that I inserted by mistake. (Most often it’s the linked version that I pasted by mistake, so not like I just have to remove a # or something like that.)
  6. I hit Ctrl+Shift+V which I should have done the first time to paste the unformatted text.

Obviously this is in part user error, and when I don’t mess up it’s only two steps (copy from other website, paste as plain text in Discourse). But when I do mess up (which is often since I’m used to just hitting Ctrl+V given that most websites don’t do formatted pasting) it would be nice if I could save some time by Ctrl+Z working like normal.

1 Like

This is generally a native browser behavior, and isn’t something Discourse is doing. Try testing against a plain text box in HTML.

Right, undo doesn’t work by default whenever you use JavaScript to modify an input. But I googled around and found that document.execCommand can insert text while appending to the undo stack.

For example, if you do document.getElementById('myInput').value = 'asd' and then Ctrl+Z, it won’t undo.

But if you do document.execCommand('insertText', false, 'asd') while the cursor is where you want it (which it should be based on the current Discourse workflow), the text is properly inserted and Ctrl+Z will remove the added text as expected.

Basically, I’m wondering if document.execCommand (or some other process if another approach is considered better) can be used to append to the undo stack so Ctrl+Z works in these cases.

2 Likes

No — we intentionally pulled that support from Discourse years ago in favor of letting the browser native text box undo handling work as it should across all standard websites.

I think I must be missing something. What does “letting the browser native text box undo handling work” mean exactly? As far as I can tell, undo for formatted text doesn’t work at all in Discourse, so are you saying that undo not working is standard website behavior?

The reason I’m confused is because I can’t think of a single website (other than things like Microsoft Word where undo works) that supports formatted paste besides Discourse. So I have nothing to compare Discourse to in order to see what is “standard”. If you could point me towards a few websites for comparison that would be really helpful.

2 Likes

See

Press Try it yourself, then enter text in the textbox, pause, then press ctrl+z to undo your actions. Here’s a demo. First we press the Try it yourself button, which results in a HTML <textarea> being displayed in the browser.

image

I enter some text into the textbox. As you can see in the screenshot, I typed

I JUST TYPED THIS TEXT YAYYYY!

image

Now, after typing, I press ctrl+z to undo my typing, and I see this:

image

Note that the text has returned to its previous state, and this was 100% handled by the browser itself, zero JavaScript code is involved.

My understanding is that @seanblue is asking if we can amend the browser APIs we use when we manipulate the textarea to better hint the browser so it can handle undo a bit better. So this would only apply to keyboard shortcuts, and toolbar, upload and stuff like that

I am not against tuning stuff here, but I worry that some off these APIs will require lots of care, there is certainly risk of regressions

Not against experiments here, maybe if the community wish to send through some PRs to show us how it is done

6 Likes

We could definitely do better here. A lot of our toolbar buttons and fancy-paste behaviour directly set the value of the textarea in JavaScript. This totally breaks the native browser undo/redo history.

Instead, whenever we make programmatic modifications to the textarea, we should be using document.execCommand (as @seanblue mentioned). That way, the browser interprets it the same as a user action, and inserts it cleanly into the undo/redo history.

the insertText command, which you can use to programmatically replace text at the cursor while preserving the undo buffer (edit history) in plain textarea and input elements.

7 Likes

But that textbox doesn’t handle formatted text, which isn’t what I’m talking about. I know that browsers handle undoing normally typed and pasted text. My point is that Discourse doesn’t handle undoing pasted formatted text. Follow these steps to see what I’m talking about.

First, open the Discourse composer:

Now copy the following text and paste it into the composer: this is a test

Now hit ctrl+z, and the pasted text is removed. This is identical to the behavior you demonstrated in your post.

Now copy the following text instead and paste it into the composer: this is a great test
Notice that it pastes it with the markdown to italicize “great”.

Now hit ctrl+z and notice that the pasted text is still there. This is what I’ve been talking about.


That’s right. I’m not suggesting handling the undo yourself in JavaScript. I’m suggesting that when you manipulate the textarea, let the browser know so it can undo the change itself when the user hits ctrl+z.

For what it’s worth, 99% of my frustration would be resolved if ctrl+z worked after pasting formatted text. Would it be ideal if every single operation could be undone with ctrl+z? Sure. But most other operations can be undone by repeating the original operation (e.g. doing ctrl+b can both add and remove bold markdown). But with paste, it has the potential to include a significant amount of unexpected markdown, including headers, links, and even tables, which is why it’s so important that undo works in that case.

If we narrowed the scope to just handling undo in the formatted paste case and ignored other shortcuts, toolbar buttons, etc., would that reduce the risk enough to give it a shot?

5 Likes

I see; between your and @david’s explanation, I understand the distinction. I just never really use those buttons in the editor for the most part. I type things in the textbox using my computer’s keyboard (either physical or on-screen), and this is handled seamlessly by the browser.

5 Likes

That’s understandable. I also tend to type the markdown instead of using the toolbar buttons for that, so that aspect isn’t really an issue for me. I only mentioned the toolbar stuff in the OP to point out that it doesn’t just happen when pasting formatted text. Being able to undo the toolbar actions isn’t super important because the user is doing those actions intentionally. But when pasting, the formatting is often incidental and unexpected, so being able to undo that would be very convenient.

4 Likes