microsoft / microsoft/WinAppVSCE
[Bug]: Manifest visual editor loses tab/scroll state when the XML is edited in the text editor
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13
- Forks
- 3
- Avg merge
- 6d 1h
- Merged PRs (30d)
- 11
Description
Describe the bug
With an AppxManifest.xml open in both the visual manifest editor and the XML text editor, editing and saving the XML causes the visual editor to lose its UI state — the selected top-level tab, the selected per-application sub-tab (Info / Extensions / Visual Assets), any optional fields the user had expanded, and the scroll position.
Confusingly, it only happens for some edits. Changing an attribute value (e.g. Properties/DisplayName, Identity/Version) preserves state. Adding or removing a <Capability> element resets the editor back to the Identity tab / Info sub-tab.
There are two distinct root causes.
Root cause 1 — the webview document is torn down and rebuilt on transiently-invalid XML
vscode.workspace.onDidChangeTextDocument fires on every keystroke, not just on save (src/manifest-editor/manifest-editor-provider.ts:169-182).
If the document fails to parse at any intermediate moment, tryParseOrShowError replaces the whole webview document with the parse-error page (manifest-editor-provider.ts:74-86). Once the XML parses again, showEditorView() assigns a brand-new webviewPanel.webview.html (manifest-editor-provider.ts:89-93).
That is a full script-context teardown, so every piece of state held in webview JS memory is destroyed:
| State | Where it lives | Survives valid→valid edit | Survives valid→invalid→valid edit |
|---|---|---|---|
| Top-level tab | DOM classes | ✅ | ❌ resets to Identity |
| App sub-tab (Info / Extensions / Visual Assets) | activeAppSubTabs — src/manifest-editor/webview-script.ts:24 |
✅ | ❌ resets to Info |
| User-opened optional fields | userOpenedOptionalFields — webview-script.ts:26 |
✅ | ❌ collapse again |
| Scroll position | DOM | ❌ (see root cause 2) | ❌ |
This explains the asymmetry. Verified against @xmldom/xmldom with the same parseManifestXml error handling the extension uses (src/manifest-schema/xml-parser.ts:33):
adding a <Capability> element editing an attribute value
----------------------------- -------------------------
INVALID "<" valid ""
INVALID "<C" valid "1"
INVALID "<Cap" valid "1."
INVALID "<Capability" valid "1.0"
INVALID "<Capability "
INVALID "<Capability Name="
INVALID "<Capability Name=\"x\""
valid "<Capability Name=\"x\" />"
Typing an element passes through many invalid states, each one flipping the webview to the error page and back. Editing a value in place never breaks the XML, so nothing is torn down.
Note that the state is also lost on a genuine VS Code reload, because the webview never persists anything via vscode.setState() (no setState/getState calls anywhere in src/manifest-editor/).
Root cause 2 — scroll position resets on every external edit
.tab-content is the scroll container (overflow-y: auto, src/manifest-editor/webview-styles.ts:76), and renderApplications starts with container.innerHTML = '' (src/manifest-editor/webview-script-applications.ts:71-73). Clearing the list collapses the scroll height, the browser clamps scrollTop to 0, and the subsequent re-render leaves it there. The same pattern applies to the dependencies and resources renderers.
Additionally, external edits call updateWebview(true), and populateForm sets focused = null when forceAll is set (webview-script.ts:283), so focus restore is skipped entirely for these updates.
Two smaller issues found while investigating
- The parse-error page listens for a
retryParsemessage (src/manifest-editor/webview-content.ts:60), but nothing in the extension ever posts it — dead code. The provider callsshowEditorView()directly instead. - When a tab is hidden based on package type, only the
.tab-contenthasactiveremoved; the corresponding.tab-btnkeeps itsactiveclass (webview-script.ts:393-395,:403-405,:413-415vs. the fallback at:417-422). Two tab buttons can end up looking selected at the same time.
To Reproduce
- Open an
AppxManifest.xmlin the visual manifest editor. - Go to the Applications tab and select the Visual Assets sub-tab.
- Open the same file in the XML text editor (
View XML/Open With… > Text Editor). - Type a new
<Capability Name="internetClient" />line inside<Capabilities>(or delete an existing one character by character), then save. Ctrl+Tabback to the visual editor.
Observed: the editor is back on the Identity tab; the Applications sub-tab has reverted to Info; expanded optional fields are collapsed; scroll is at the top.
For contrast, repeat with an edit that keeps the XML valid at every keystroke — e.g. changing Identity/@Version from 1.0.0.0 to 1.0.0.1. The tab and sub-tab selection are preserved (though scroll still resets).
Expected behavior
Switching back to the visual editor after an external XML edit should leave the user exactly where they were: same top-level tab, same per-application sub-tab, same expanded optional fields, same scroll position. Transiently-invalid XML while the user is mid-keystroke in the text editor should not reset the visual editor's UI state.
Suggested fix
- Stop reassigning
webview.htmlfor parse failures — render the parse error as an overlay inside the existing document (and/or debounce it) so transient invalid states never tear down the script context. - Persist UI state (
activeAppSubTabs, active top-level tab,userOpenedOptionalFields, scroll offsets) viavscode.setState()/getState()so it also survives a genuine webview reload. - Save and restore
scrollToparound the list re-renders inrenderApplications/renderResources/ the dependency renderers. - While in there: remove the dead
retryParsehandler and keep.tab-btn.activein sync with.tab-content.activewhen tabs are hidden by package type.
Screenshots
N/A
OS Version and details
Windows 11
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/manifest-editor/manifest-editor-provider.ts, webview-script.ts, webview-script-applications.ts, webview-content.ts, and webview-styles.ts. Trace external document updates through parse-error handling and the list renderers, then determine how existing webview state and scroll offsets can be preserved. Done means external edits, including transiently invalid XML, retain tabs, expanded fields, focus, and scroll position without inconsistent tab buttons.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 52/100