Repository navigation
Make the Undo button inert when there is nothing to undo - #6082
Conversation
#5994) Co-authored-by: BayuAri <8419585+BayuAri@users.noreply.github.com>
Preview Deployment · Oct 23, 2026, 12:14 AM UTC · 5a5ac4f |
Restore canExecute on undo so the greyed-out toolbar and Command Center buttons are inert, and set preventDefault so Cmd+Z still always blocks native undo (#3879). Co-Authored-By: GitHub Copilot Coding Agent unknown (unknown) <223556219+Copilot@users.noreply.github.com> Co-authored-by: BayuAri <8419585+BayuAri@users.noreply.github.com>
BayuAri
left a comment
There was a problem hiding this comment.
Confirmed fixed.
Undo button is disabled on first open and when there is no Undo Stack to execute accordingly
| if (!isUndoEnabled(getState())) return | ||
| dispatch(undo()) | ||
| }, | ||
| canExecute: state => isUndoEnabled(state), |
There was a problem hiding this comment.
This change results in the Undo toolbar button being disabled for ~500ms on the first edit due to the text edit throttling.
Can you please resolve this? One option to consider is updating Editable.tsx to commit the first edit immediately by adding isUndoEnabled as one of the conditions here:
em/src/components/Editable.tsx
Lines 848 to 855 in a80340a
Case to reproduce below (please also add as a test case, if feasible... not sure if we have a nice way to simulate reloading the app / starting with a thought but an empty history):
- Create a thought
Hello - Reload the application
- Place the cursor at the end of
Helloand typeW(becomingHelloW)
Result: The "Undo" button is disabled for ~500ms until becoming active and working
Expected: The "Undo" button should become immediately active, as it does in main.
There was a problem hiding this comment.
Fixed in 3f1deef. I used your suggestion: Editable now commits an edit immediately when !isUndoEnabled(state), so the first keystroke after a reload enables Undo right away.
- Undo grouping: unchanged. Typing
Worldafter a reload and tapping Undo once still returns toHello, because the undo history merges the immediate edit with the throttled edits that follow it. - Test: added to
src/e2e/puppeteer/__tests__/undo.ts(mobile, persistent storage). It reloads, typesWat the end ofHello, taps Undo right away, and expectsHello. Before the fix it failed withexpected 'HelloW' to be 'Hello'. - Side effect on desktop: the first keystroke after a reload now starts the distraction-free toolbar fade about 500ms sooner, the same as edits that already commit immediately (URLs, formatting).
- Docs:
docs/commands.md"Flushing pending edits" now describes this exception to the throttle.
|
@copilot - Can you please address this review comment and resolve the defect? |
Co-Authored-By: GitHub Copilot Coding Agent unknown (unknown) <223556219+Copilot@users.noreply.github.com> Co-authored-by: trevinhofmann <5055041+trevinhofmann@users.noreply.github.com>
Undo's canExecute reads the undo history, so the edit throttle left the Undo button disabled for ~500ms after the first edit following a reload. Co-Authored-By: GitHub Copilot Coding Agent unknown (unknown) <223556219+Copilot@users.noreply.github.com> Co-authored-by: trevinhofmann <5055041+trevinhofmann@users.noreply.github.com>
The Lottie animation is a side effect of the enabled state, and the tests were big, slow, and coupled to animation details. Co-Authored-By: Claude claude-opus-5-5 (medium) <noreply@anthropic.com>
| }) | ||
|
|
||
| // https://github.com/cybersemics/em/issues/5994 | ||
| it('does not animate the greyed-out Undo button when there is nothing to undo', async () => { |
There was a problem hiding this comment.
The Lottie animation is a side effect of the undo button being enabled when it shouldn’t. While it makes sense as a proxy for the actual issue, it is not a good candidate for automated testing. It’s big and slow, and too tightly coupled to animation implementation details. Unfortunately the agent didn’t pick up on this distinction.
I would completey remove both of these tests.

Tapping the greyed-out Undo button with an empty undo stack still played the undo animation. It should do nothing on any platform while the stack is empty.
Cause
Since #4013,
undodeclaresisActive: isUndoEnabledin place ofcanExecute. WithoutcanExecute, the command always counts as executable, so Cmd+Z always callspreventDefaultand blocks native undo (#3879). Two side effects on the toolbar:ToolbarButtonanimates a command that hasisActiveonly when it goes from inactive to active. As a result, a real undo never animated, and the animation only ever played on the wrong tap.Change
src/commands/undo.ts:canExecute: isUndoEnabled.preventDefault: trueflag, whichkeyDownhonours even whencanExecuteis false, so Cmd+Z still always swallows native undo.isActive.Resulting behaviour:
handleNativeHistorychecksisUndoEnableddirectly) and long-press opening the UndoSlider.Test
src/e2e/puppeteer/__tests__/undo.ts(mobile):MutationObserver, because under PuppeteerLottieAnimationjumps to its last frame and unmounts as soon as it's ready. Reading the DOM after the tap would miss it.