Skip to content

Navigation as undo/redo #276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
KevinDoughty opened this issue Sep 16, 2024 · 0 comments
Open

Navigation as undo/redo #276

KevinDoughty opened this issue Sep 16, 2024 · 0 comments

Comments

@KevinDoughty
Copy link

This is a request for accomodations to use navigation as undo/redo in document-based productivity web apps. Whether this is a crucial feature or an abomination is open for debate.

The argument for using navigation for undo and redo is that location traversal is the wrong paradigm for document-based web apps. The only useful navigation is to changes of user state. An argument against is (supposedly) subverting user expectations which this proposal intends to debunk. Prior opposition has already been suggested. #160 (comment)

The first requested accomodation is to implement “Updating, deleting, and rearranging non-current entries” (#9). Target URLs need to differ depending on direction traveled in the navigation stack. The URLs of mutliple entries would need to be changed simultaneously without triggering a navigation. The reason requires nuanced explanation which will follow.

The second requested accomodation is the ability to specify an undo description to be displayed in the list of navigable entries, instead of using document.title. This was formerly possible using the second parameter to history pushState. A workaround is temporarily setting the title then reverting afterward, which occasionally gives a flash-of-undo-description wherever the title is displayed. Like URLs, undo descriptions would be replaced by redo descriptions on navigation, and vice-versa.

A third accomodation would be visually demarcating the list of entries as shown by long press of back and forward buttons on desktop. This could be achieved through indentation, horizontal separators, or color change. Entries could be grouped automatically, or by some manual method.

For document-based apps, the user is better served by navigating state change. Implementations of location-based navigation are convoluted and vary drastically from app to app across the web. The pattern for navigating state instead of location is one which could be consistently implemented across different apps. It can be achieved through use of two separate data models, undoables and preservables.

Undoables are for user state that would typically be archived in some way. On value change, they would be registered by appending an entry to the navigation stack. Undoables may or may not coalesce changes into one entry, which is useful for continuous changes to controls like sliders.

Preservables are for appearance and presentation. They would not themselves add an entry to the navigation stack on change, but as their name suggests would be preserved and restored on change to undoables. Preservable state, it is argued, solves all use cases for document based apps currently forced to use navigation of location. Any edge cases can be discussed, of which there are presumably many, particularly on mobile.

This is where the magic happens. The value of preservables as restored by navigating the undo stack depends on the direction traveled. The value of a preservable when reaching a certain state of an undoable via undo may be different when reaching the very same state of an undoable via redo. This is accomplished by capturing the value of all preservables both before and after a change to an undoable.

If preservable state were only captured once, undoable state changes would not be visible to the user on navigation. They would be taken to a new location every time. As proposed, preservables enable the user to repeatedly hit undo-redo-undo-redo to see where changes happen without changing location. This behavior occurs naturally to users and would not need to be documented. Its absence is the greater subversion of user expectation.

For document-based web apps, the first and foremost preservable should be the URL. While preservables are likely not archived, the user could restore appearance with URLs which is an advantage held over native apps.

The following two examples, despite being trivial, lacking visual design, and not using browser navigation as proposed, do show preservables in action.

https://kevindoughty.gitlab.io/hello-undo/

Press Undoable once, then press Preservable five times, then press Undoable once more. State is { u:2, p:5 }. Undo gives { u:1, p:5 }. Undo again gives { u:0, p:0 }. Now redo gives { u:1, p:0 }. When u = 1, p is either 0 or 5 depending on direction. Now, undo all the way to the start. Press Preservable three times. State is { u:0, p:3 }. On redo, state is { u:1, p:0 }. An undo following that restores state to { u:0, p:0 }. The state where p = 3 is lost.

https://kevindoughty.gitlab.io/cute-tree/

Select the most deeply-nested element and change its text. Collapse its parent node so the newly edited text is not visible. Next, change the text of any other visible element. On undo, that second edit is reverted. A second undo expands the recently collapsed parent to show the first edit has been reverted. The user cannot actually tell what has changed until they hit redo again which restores the first edit, but without being hidden by the collapsed parent. Now, undo to the start. The user can see what changed. Next, collapse many or all elements. On redo, they are all expanded and the first edit is restored, and the state where all elements are collapsed is lost.

The preservable state in the first example of 0, 5, or 3 and collapsed/expanded disclosure triangle state from the second correspond to any combination of UI elements, loaded in an iframe or not. The user should be unburdened from having to traverse child navigables, or any location.

The scenario explained in “Setting the current entry’s state without navigating” (navigation-api) is insufficent for document-based web apps. Completely detatching user state, by traversing location while retaining separate undo and redo buttons, would complicate the interface and confuse the user. A web app could replace every navigation for a single location entry in the stack to implement undo, but preservables would serve the user better.

The existence of document-ish or other user state that benefits from navigation of location is not disputed, but these cases should not allow more than one state of undo or redo. This would allow the same undo-redo-undo-redo pattern without navigating anywhere but to the change location. Undoable web apps that navigate user state could restrict the stack to ten or twenty entries.

Navigating trees of locations is just plain wrong for document-based apps, but if the list of entries could be demarcated by indentation, other types of apps could benefit.

The problem of opening a new tab or window that preserves navigation entries is no different from editing multiple copies of a document in native apps. It might be resolved with steps taken by app authors or browser vendors, and remains open for further discussion. The effect of asynchronous loading and how to handle it might also require further discussion.

The purpose of productivity apps is inherently to create and edit documents instead of browsing them. The choice of navigating changes to state over location should be clear, and accomodations should be made to allow it.

What follows is a simplification of what the list of entries might look like. Location and state navigation are mixed.

[The bees and me.](https://superbad.com/1/bee/bee.html)
[Undo change zero to asdf](http://localhost:5173/zero)
[Undo change five to qwer](http://localhost:5173/five)
[A document-based web app](http://localhost:5173/five) // current

After clicking the backward button:

[The bees and me.](https://superbad.com/1/bee/bee.html)
[Undo change zero to asdf](http://localhost:5173/zero)
[A document-based web app](http://localhost:5173/five) // current
[Redo change five to qwer](http://localhost:5173/five)

After clicking the bacward button again:

[The bees and me.](https://superbad.com/1/bee/bee.html)
[A document-based web app](http://localhost:5173/zero) // current
[Redo change zero to asdf](http://localhost:5173/zero)
[Redo change five to qwer](http://localhost:5173/five)

The URL at index 2 has changed in preparation for redo. Navigating somewhere else by clicking a link updates the entry at index 1:

[The bees and me.](https://superbad.com/1/bee/bee.html)
[A document-based web app](http://localhost:5173/three) // current
[Redo change zero to asdf](http://localhost:5173/zero)
[Redo change five to qwer](http://localhost:5173/five)

On redo the correct URL at index 1 is restored in preparation for undo:

[The bees and me.](https://superbad.com/1/bee/bee.html)
[Undo change zero to asdf](http://localhost:5173/zero)
[A document-based web app](http://localhost:5173/zero) // current
[Redo change five to qwer](http://localhost:5173/five)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant