Skip to content

Keep a JSON dictionary's Modified marks when one of its tabs closes - #31

Merged
ww-mw merged 1 commit into
mainfrom
fix-23-two-tab-baseline
Sep 22, 2026
Merged

ww-mw merged 1 commit into
mainfrom
fix-23-two-tab-baseline

Conversation

@ww-mw

@ww-mw ww-mw commented Sep 22, 2026

Copy link
Copy Markdown
Member

Part of #23 — the half of it that is a plain bug rather than a product decision.

The bug

SlddTextEditorProvider tore down two pieces of document state from
webviewPanel.onDidDispose, which fires per panel:

state keyed by owner
the "Modified" baseline (slddBaseline) URI the document
the cross-document-move source deleter (editorHub) URI the document

So opening one .sldd in two tabs (or splitting it) and closing one left the surviving
tab with no baseline — and with no baseline computeModified answers "nothing is modified"
by design, so every Modified mark vanished from a tab the user was still editing, unsaved
edits intact underneath. The same dispose dropped the source deleter, so a move dragged out
of the surviving tab silently failed to delete its originals.

The on-open capture had the same panel/document confusion in the other direction:
initialized was a local of resolveCustomTextEditor, so each tab believed it owed the
capture, and captureBaseline overwrites by URI. Opening a second tab on an
already-edited dictionary re-baselined it against the edited text and cleared the marks in
every tab at once.

The fix

BinarySlddEditorProvider already carries both facts on its custom document
(views / baselineCaptured, teardown guarded by document.views.size === 0) and its
comment names the same two failures. A CustomTextEditorProvider has no custom document
object to hang state on, so the same two facts now live in a URI-keyed openDocs map in
SlddTextEditorProvider.ts, and the teardown runs only when a document's last tab
closes. One rule, both paths.

Tests

test-integration/suite/twoTabBaseline.test.ts — real provider, real VS Code, two panels on
one document. Both assertions fail on the previous code:

  • the second tab shows the edit as Modified, and opening it does not clear the first tab's
    marks; after the first tab closes the survivor still reports Status: "Modified"
  • deleteFromSource still completes after one of two tabs closes

No existing test was modified. npm run verify green (2480 unit tests, typecheck, both
bundles, leak check).

Qualified in the browser

Guardrail: a visible fix is not landed until it is checked from the customer's side. Measured
against the shipped dist/webview bundle in Chromium with VS Code's real palette, rows built
by the real buildRows() from a real .sldd — the state this fix restores vs. the state the
bug left behind, which differ only in the modifiedNames set the host posts:

theme baseline survived (fixed) baseline cleared (bug)
Dark Modern 1 visible "Modified" mark 0
Light Modern 1 0
HC Black 1 0
HC Light 1 0

No console errors, no failed requests. Screenshots in /tmp/dex-harness/issue23-*.png.

Out of scope, captured for the maintainer rather than changed here: the mark's colour is the
hard-coded #b45309 with no theme token, which measures 3.54 on Dark Modern and 4.18 on
HC Black — both below WCAG AA
for normal text. Choosing a token is a design decision, so
it is on the daytime agenda, not in this PR.

Still open on #23

The reporter's own video is most likely the other mechanism: "Modified" means differs from
the last save
, and with files.autoSave: afterDelay (1000 ms default) the file saves about
a second after typing stops, the baseline is re-captured, and the marks clear on their own.
That behaviour is pinned by two existing tests and changing it is a product decision, so #23
stays open with agent:needs-discussion.

SlddTextEditorProvider tore down two pieces of DOCUMENT state from
webviewPanel.onDidDispose, which fires per PANEL: the per-URI "Modified"
baseline (slddBaseline) and the cross-document-move source deleter
(editorHub). Open one .sldd in two tabs or split it, close one, and the
surviving tab lost every Modified mark while the document stayed open and
edited — with no baseline, computeModified answers "nothing is modified" by
design — and a move dragged out of that tab could no longer delete its
originals.

The on-open capture had the same panel/document confusion in the other
direction: `initialized` was a local of resolveCustomTextEditor, so each tab
believed it owed the capture, and captureBaseline OVERWRITES by URI. Opening a
second tab on an already-edited dictionary therefore re-baselined against the
edited text and cleared the marks in every tab at once.

BinarySlddEditorProvider already carries both facts on its custom document
(`views` / `baselineCaptured`) for exactly these reasons; a CustomTextEditorProvider
has no such object, so the same two facts now live in a URI-keyed openDocs map,
and the teardown runs only when the last tab of a document closes.

Pinned by test-integration/suite/twoTabBaseline.test.ts, which drives the real
provider in real VS Code with two panels on one document: the surviving tab
still reports Status "Modified" after the other closes, and deleteFromSource
still completes. Both assertions fail on the previous code.
@ww-mw
ww-mw merged commit 97534be into main Sep 22, 2026
1 check passed
@ww-mw
ww-mw deleted the fix-23-two-tab-baseline branch September 22, 2026 06:36
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