What
Each webview carries a stable view-state id (viewStateId) that the extension uses to key its durable per-view state buffer. Today the webviewDidLaunch message does not persist that id on the provider, so per-view state is not pinned to the launching webview, and launch-time validation of the view-local API profile can fall back to mutating the shared global selection instead of re-pinning the local view.
The gap has three parts:
- Identity not persisted at launch. The webview has no stable id helper —
getState()/setState() have no fallback outside the VS Code webview host, and nothing creates or persists a viewStateId. The launch effect posts webviewDidLaunch without an id, and the handler never calls provider.setViewStateId(...), so the pre-launch temporary state entry is never re-keyed to the webview's identity.
- Launch re-pin mutates the shared global. When the view-local
currentApiConfigName is invalid at launch, the handler resets the shared global currentApiConfigName to the first listed profile and activates it globally. When the shared global selection is still a valid profile, the view should be re-pinned to it with a view-local write only, leaving the global selection untouched.
updateSettings bypasses the view-local mutation path. Settings updates write through contextProxy.setValue directly instead of provider.setValue, so the view-local buffer/pin sync that the other mutation paths perform does not run for settings edits.
Repro
- Open two Zoo Code windows (or two webviews) with different per-view API profiles.
- Delete the profile that one view's local pin references.
- Reload that view.
The view-local pin is repaired by resetting the shared global profile selection (observable in the other view) rather than re-pinning the local view to the still-valid shared selection.
Expected
webviewDidLaunch carries a stable viewStateId from the webview (created and persisted via the webview state API, with an in-memory fallback when storage is unavailable), and the handler persists it via provider.setViewStateId so per-view state is keyed to the launching webview.
- Launch-time profile validation re-pins the view locally when the shared global selection is still valid; it only repairs the global selection when the global selection is also invalid.
updateSettings routes through provider.setValue so view-local buffer and pin sync stay consistent with the other mutation paths.
Proposed fix
- webview-ui: add
getViewStateId() to VSCodeAPIWrapper (persisted via setState, in-memory fallback) and post the id from the launch effect.
- extension: on
webviewDidLaunch, call provider.setViewStateId(viewStateId) and re-pin the view-local currentApiConfigName through provider.saveViewState when the shared global is still valid; route updateSettings through provider.setValue.
- tests: webview-ui unit spec for the id helper (storage persistence, fallback, id shape), webview-ui spec for the launch post, and webview-message-handler integration tests for the launch validation/re-pin behavior.
Part of the vps2 durable per-view state series — tracked in easonLiangWorldedtech#41.
What
Each webview carries a stable view-state id (
viewStateId) that the extension uses to key its durable per-view state buffer. Today thewebviewDidLaunchmessage does not persist that id on the provider, so per-view state is not pinned to the launching webview, and launch-time validation of the view-local API profile can fall back to mutating the shared global selection instead of re-pinning the local view.The gap has three parts:
getState()/setState()have no fallback outside the VS Code webview host, and nothing creates or persists aviewStateId. The launch effect postswebviewDidLaunchwithout an id, and the handler never callsprovider.setViewStateId(...), so the pre-launch temporary state entry is never re-keyed to the webview's identity.currentApiConfigNameis invalid at launch, the handler resets the shared globalcurrentApiConfigNameto the first listed profile and activates it globally. When the shared global selection is still a valid profile, the view should be re-pinned to it with a view-local write only, leaving the global selection untouched.updateSettingsbypasses the view-local mutation path. Settings updates write throughcontextProxy.setValuedirectly instead ofprovider.setValue, so the view-local buffer/pin sync that the other mutation paths perform does not run for settings edits.Repro
The view-local pin is repaired by resetting the shared global profile selection (observable in the other view) rather than re-pinning the local view to the still-valid shared selection.
Expected
webviewDidLaunchcarries a stableviewStateIdfrom the webview (created and persisted via the webview state API, with an in-memory fallback when storage is unavailable), and the handler persists it viaprovider.setViewStateIdso per-view state is keyed to the launching webview.updateSettingsroutes throughprovider.setValueso view-local buffer and pin sync stay consistent with the other mutation paths.Proposed fix
getViewStateId()toVSCodeAPIWrapper(persisted viasetState, in-memory fallback) and post the id from the launch effect.webviewDidLaunch, callprovider.setViewStateId(viewStateId)and re-pin the view-localcurrentApiConfigNamethroughprovider.saveViewStatewhen the shared global is still valid; routeupdateSettingsthroughprovider.setValue.Part of the vps2 durable per-view state series — tracked in easonLiangWorldedtech#41.