Skip to content

fix(model): hold an undo pressed while the copy is brought up to date - #173

Merged
aicayzer merged 6 commits into
mainfrom
fix/undo-during-refresh
Oct 26, 2026
Merged

aicayzer merged 6 commits into
mainfrom
fix/undo-during-refresh

Conversation

@aicayzer

@aicayzer aicayzer commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

What changes for a person

An Undo or Redo pressed while the library is catching up, as after an item is moved, is no longer refused and lost. MAPP holds it and makes it once, when the library is whole again.

  • If the items it touches are unchanged, it is made automatically and exactly once.
  • If they changed meanwhile (for example on another device), it is not made, so newer text is never overwritten. The person is told, and Undo stays available to choose again as things now are.
  • If the library never catches up (offline), the held Undo is given back after 30 seconds and the person is told. It stays available.
  • A Redo that cannot be made cannot be put back on the stack. The Undo it registered then writes nothing and says so.

What I chose

  • The hold is in the engine, not the library. The undo manager has already taken the step off its stack, and the package's refusal rule (hydrationIncomplete, copyExpired) is the one source of truth for when a write cannot be made yet.
  • Writes of a running Undo or Redo carry a group (ItemSource.submit(_:as:undoing:)). The engine holds a marked write that the package refuses that way and releases it after a read that found the copy whole. Each write is rebased on the item's current version, and judged first on whether its item, or a link's ends and type, are as they were when Undo was pressed.
  • Closing the library answers held writes as closed. Held steps are not kept across a quit.

Limits

  • Only writes the package refuses before queueing are held: an edge write, and an edit to a row the copy does not hold yet. The package queues an item's edit to a row it holds at once.
  • While any write is held, each later write of an Undo or Redo waits behind it unattempted, so steps are made in order. A step is made in part only where a write the package queues at once comes before one it refuses, since that earlier write is already made.
  • Each write is judged against what the engine held when it handled the write, in the order submitted. The catch-up's read is queued behind the writes already submitted, so a change it finds cannot hide from the later writes of the step. Taking the stand at the key press itself would need the engine's state from the main thread, and was not done.
  • A step of many writes asks the copy to catch up once, not once for each write.
  • A Redo that is not made cannot be put back; if its write carried the person's text, the text goes to the clipboard through the not-kept notice.
  • Held steps are not kept across a quit.

Tests

  • The catch-up fake now refuses as the package does: edge writes, and edits to rows not held, throw; edits to held rows queue.
  • Added: an unlink plus a property edit made in order and once; an undo then a redo held and made in order; an edit after a held undo waits behind it; a step whose later write touches a changed item is made in no part; 200 held writes cause one catch-up and almost no full reads; a Redo not made gives back its text.
  • EditorTests/selectingAnotherItemReturnsTheBodyToReading needed a key window that the background-only host cannot give (it failed alone on main too). It now checks the editor's own focus state with no panels. The key-window checks were its only window behaviour and are removed.
  • Unit plan: passes in full. The Live and UI plans were not run.

@aicayzer
aicayzer merged commit 878622d into main Oct 26, 2026
4 checks passed
@aicayzer
aicayzer deleted the fix/undo-during-refresh branch October 26, 2026 18:15
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

Successfully merging this pull request may close these issues.

1 participant