Skip to content

Add ability to control native undo/redo stack #509

@michael

Description

@michael

The problem: inputType historyUndo/historyRedo unusable for editors that maintain their own document state

I was excited to learn that I'm now able to intercept historyUndo and historyRedo inputTypes at beforeinput stage.

But unfortunately I can't utilize them because they are only fired when there are items on the browser-native undo/redo stacks. 😢

Here's a scenario from Svedit:

  • user enters a character (I handle this at beforeinput and add a character to my internal model, which triggers a rerender and updates them DOM)
  • now i go to Edit > Undo in the browser
  • nothing happens because the browser didn't get a history event, as there's nothing on the browser-native undo stack (because I handled everything myself)

There's no way for me to "fake native history entries" as I would always need to let some input slip through and mess with the DOM in order to be recognized as a change that gets added to the native undo history.

I see two directions to solve the problem:

  • historyUndo/historyRedo could be triggered always (even though the browser undo stack is empty, so they are more like an "historyUndoRequested" input). Then I get the chance to handle this in my model and run undo (because there I know there's a change that can be undone).
    • Only when I don't handle historyUndo/historyRedo (+ call preventDefault) the browser's native undo would run (e.g. when I'm in a text area outside of the contenteditable)
  • Programmatic access to create "custom history entries" in the browser programmatically. This means that basically each time I apply a transaction in my model, i can call an API ala document.addHistoryEntry(), without a payload, just to keep the native history stack in sync with my app-specific one. Now I could handle the native historyUndo/historyRedo also for my custom history entries. If not handled, they'd result in a no-op. However I may also sometimes call undo/redo programmatically (undo/redo buttons in a toolbar), which would then just work with document.executeCommand('undo|redo') I guess?

Without having had a deep reflection on this, my gut says the first option is enough (and more straightforward). Though the second option would allow a more deep integration for custom apps with an undo/redo stack (those app may not even use contenteditable).

A lot of webtools suffer from that issue. E.g. when you edit a file in figma.com, and you use the native "Edit > Undo" nothing happens. You have to use the keybindings, or the menu Figma renders in the browser.

Metadata

Metadata

Assignees

No one assigned

    Labels

    TPAC2025Topics for discussion at TPAC 2025

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions