microsoft / microsoft/WinAppVSCE

[Bug]: Manifest visual editor loses tab/scroll state when the XML is edited in the text editor

Open
#192 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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) activeAppSubTabssrc/manifest-editor/webview-script.ts:24 ❌ resets to Info
User-opened optional fields userOpenedOptionalFieldswebview-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 retryParse message (src/manifest-editor/webview-content.ts:60), but nothing in the extension ever posts it — dead code. The provider calls showEditorView() directly instead.
  • When a tab is hidden based on package type, only the .tab-content has active removed; the corresponding .tab-btn keeps its active class (webview-script.ts:393-395, :403-405, :413-415 vs. the fallback at :417-422). Two tab buttons can end up looking selected at the same time.
To Reproduce
  1. Open an AppxManifest.xml in the visual manifest editor.
  2. Go to the Applications tab and select the Visual Assets sub-tab.
  3. Open the same file in the XML text editor (View XML / Open With… > Text Editor).
  4. Type a new <Capability Name="internetClient" /> line inside <Capabilities> (or delete an existing one character by character), then save.
  5. Ctrl+Tab back 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
  1. Stop reassigning webview.html for 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.
  2. Persist UI state (activeAppSubTabs, active top-level tab, userOpenedOptionalFields, scroll offsets) via vscode.setState() / getState() so it also survives a genuine webview reload.
  3. Save and restore scrollTop around the list re-renders in renderApplications / renderResources / the dependency renderers.
  4. While in there: remove the dead retryParse handler and keep .tab-btn.active in sync with .tab-content.active when tabs are hidden by package type.
Screenshots

N/A

OS Version and details

Windows 11

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.