Preserve canvas input state across live updates - #166
Merged
Merged
Conversation
Switching back to a previously-viewed canvas doc unconditionally fired a morph that re-fetched the on-disk HTML; idiomorph then resynced form fields to that (empty) markup, wiping any in-progress user input. Track AgentDocs whose on-disk content changed while hidden in a new StaleHiddenDocs set and morph on switch-back only for those, clearing the mark on morph. An ordinary tab switch with no on-disk change now morphs nothing, so the mounted iframe's live form input survives.
Addresses focused-review findings F1-F6, all clustered on the new StaleHiddenDocs mechanism leaving marks that later drive a spurious switch-back morph and wipe in-progress form input. - src/Client/CanvasState.fs — add pure helpers markStale (only mark currently mounted-but-hidden docs) and pruneStaleToMounted (GC marks whose iframe has unmounted); single owner for the invariant (F2) - src/Client/App.fs:226 — restrict DataLoaded population to mounted-hidden docs via markStale and always GC via pruneStaleToMounted (F1) - src/Client/CanvasUpdate.fs:84 — clear the selected doc's stale mark on every reveal, not only when morphing; covers first-open and LRU-remount (F4, F3) - src/Client/CanvasUpdate.fs:135 — prune StaleHiddenDocs in the archive success branch so an orphan mark can't poison a same-name regen (F5) - src/Tests/CanvasAwarenessTests.fs:1213 — replace mutable ResizeArray test helper with a scoped let mutable accumulator (F6); add regression tests for markStale, pruneStaleToMounted, first-open clearing, and archive prune Copilot-Session: d7c38d3c-d16c-4790-bb19-59d3c3caee33
# Conflicts: # src/Tests/CanvasAwarenessTests.fs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 87093918-b9f2-4be7-a1de-d7bc8d212aac
- src/Client/App.fs:214 — preserve delayed hidden-document hash changes - src/Client/App.fs:323 — synchronize mounted iframes across pane closure - src/Client/App.fs:325 — separate iframe synchronization from notification freshness - src/Client/CanvasState.fs:89 — model actual mounted iframe hashes - src/Client/CanvasState.fs:89 — centralize mounted-state lookup - src/Client/CanvasState.fs:102 — cancel stale state when content reverts - src/Client/CanvasUpdate.fs:72 — use stale-aware open-document reveals - src/Client/CanvasUpdate.fs:75 — remove the unreachable AgentDoc guard - src/Client/CanvasUpdate.fs:89 — scope morph completion and retain failed work - src/Client/CanvasUpdate.fs:133 — centralize every canvas reveal transition - src/Client/CanvasUpdate.fs:432 — reject forged hidden-frame morph completion - src/Tests/CanvasAwarenessTests.fs:1430 — share the command-message collector - src/Tests/CanvasAwarenessTests.fs:1432 — replace accumulator iteration with recursion - src/Tests/CanvasPaneTests.fs:242 — exercise a real hidden-iframe switch-back Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 67c3e514-599b-47a8-9fde-e007873118bd
There was a problem hiding this comment.
Pull request overview
Preserves AgentDoc iframe state during live updates and avoids unnecessary morphs.
Changes:
- Restores dirty form values, focus, and selection after morphing.
- Tracks mounted iframe hashes and scopes morph messaging.
- Adds unit/E2E coverage and updates canvas specifications.
Show a summary per file
| File | Description |
|---|---|
src/Tests/CanvasPaneTests.fs |
Tests iframe persistence and message scoping. |
src/Tests/CanvasAwarenessTests.fs |
Tests mounted-hash lifecycle and synchronization. |
src/Tests/CanvasAuthoringDxE2ETests.fs |
Tests form-state restoration. |
src/Extension/canvas-morph.test.mjs |
Adds morph state unit tests. |
src/Extension/canvas-morph.js |
Implements state restoration and scoped morph completion. |
src/Client/CanvasUpdate.fs |
Centralizes reveal and morph synchronization. |
src/Client/CanvasTypes.fs |
Defines scoped morph identity. |
src/Client/CanvasState.fs |
Tracks mounted iframe hashes. |
src/Client/CanvasPane.fs |
Adds iframe identity and validates completions. |
src/Client/AppTypes.fs |
Adds payloads to morph messages. |
src/Client/App.fs |
Integrates hash reconciliation and synchronization. |
docs/spec/future/canvas-roadmap.md |
Updates the preservation roadmap. |
docs/spec/canvas-pane.md |
Documents the new synchronization model. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 13/13 changed files
- Comments generated: 4
- Review effort level: Balanced
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 14d12947-ef74-43cd-84e3-1a1ab93cc174
0101
enabled auto-merge (squash)
August 13, 2026 11:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
morph-completemessages can be attributed to the wrong document and repeatedly persist an unchanged viewed hash.Fix
<input>and<textarea>values before a morph, then restore their values, focus, and selection afterward. Untouched controls still receive new authored defaults.Tests
npm run buildnpm run test:extension— 48 passeddotnet test src\Tests\Tests.fsproj --no-build --filter "Category=Fast"— 1,901 passed