fix(editor): collapse the workspace split when the last buffer closes (T-459)

EditorRegistry.close() guarded its active-changed emit on `_activeId !=
null`, so closing the LAST buffer (active clears to null) emitted only
editor.closed — never the active-changed(id:null) the editor extension
listens for to call closeEditor(). editorOpen stayed true and the top
split sat orphaned over the Claude pane. Always emit active-changed when
the active buffer is removed, including the cleared-to-null case; the
slot renderer already collapses correctly once editorOpen flips false.

The existing extension test fabricated the null active-changed event, so
it passed despite the registry never emitting it — that gap is why the
bug shipped. Add a registry test that drives the real close() path, plus
a slot_host widget test asserting the split (drag handle) drops out and
the primary pane fills the column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-22 17:49:12 +02:00
co-authored by Claude Opus 4.8
parent 8c9c881d81
commit 1e49f3e1d9
6 changed files with 188 additions and 1 deletions
+17
View File
@@ -89,6 +89,23 @@ void main() {
expect(sink.ofKind('editor.active-changed').length, greaterThanOrEqualTo(2));
});
test('closing the last buffer clears active + emits active-changed with id=null (T-459)', () async {
final buf = await reg.open('README.md');
expect(reg.active, same(buf));
sink.events.clear();
reg.close(buf.id);
// No buffer left — active clears to null.
expect(reg.active, isNull);
// The collapse signal: active-changed must still fire, carrying a null id,
// so the editor split drops out. The bug (T-459) was this emit being
// guarded away on the last close, leaving editorOpen stuck true.
final activeChanged = sink.ofKind('editor.active-changed');
expect(activeChanged, hasLength(1));
expect(activeChanged.single.data['id'], isNull);
expect(activeChanged.single.data['path'], isNull);
expect(sink.ofKind('editor.closed'), hasLength(1));
});
test('opening a non-existent path creates an empty buffer', () async {
final buf = await reg.open('NEW.md');
expect(buf.content, isEmpty);