From b9cfd547427c4d6743da526758b6e221a7dd16ae Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Thu, 29 Oct 2026 22:42:19 +0800 Subject: [PATCH] fix(editor): clear stale text focus when switching tabs --- src/editor/core/controller.test.ts | 71 +++++++++++++++++++++++++++++- src/editor/core/controller.ts | 1 + 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/editor/core/controller.test.ts b/src/editor/core/controller.test.ts index 28ce1cfe..031c37f6 100644 --- a/src/editor/core/controller.test.ts +++ b/src/editor/core/controller.test.ts @@ -1,4 +1,4 @@ -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { EditorController, openFilePaths, useEditorStatus, useTabs } from "./controller"; import { createMemoryBackend } from "./backend"; import { initialTabsState } from "./model/tabs"; @@ -7,6 +7,9 @@ import { resetDistractionFree, setDistractionFree } from "@/workbench/distractio import { registerActions } from "@/platform/actions"; import { useProject } from "@/platform/project"; import { resolveMenuItems } from "@/ui"; +import { getContext } from "@/platform/context"; +import { installKeyboardDispatcher } from "@/platform/keymap"; +import { TEXT_WHEN } from "./actions"; let ctrl: EditorController | null = null; @@ -18,6 +21,72 @@ afterEach(() => { }); describe("EditorController", () => { + it("leaves modified Enter to a non-text editor after switching from a focused file", async () => { + ctrl = new EditorController(createMemoryBackend({ "/p/a.ts": "one\ntwo\n" })); + ctrl.start(); + await ctrl.openFile("/p/a.ts", { focus: false }); + const slot = ctrl.activeSlot!; + const host = document.createElement("div"); + document.body.appendChild(host); + const view = slot.mount(host); + ctrl.onMounted(slot); + view.focus(); + await vi.waitFor(() => expect(getContext().editorTextFocus).toBe(true)); + + const splitLine = vi.fn(); + const dispose = registerActions([ + { + id: "code.splitLine", + title: "Split Line", + when: TEXT_WHEN, + run: splitLine, + }, + ]); + const uninstall = installKeyboardDispatcher(window); + const message = document.createElement("textarea"); + const send = vi.fn(); + message.addEventListener("keydown", send); + try { + await ctrl.openInput({ kind: "agent-session", key: "task" }); + slot.unmount(); + host.appendChild(message); + message.focus(); + message.dispatchEvent( + new KeyboardEvent("keydown", { + key: "Enter", + code: "Enter", + metaKey: true, + bubbles: true, + cancelable: true, + }), + ); + expect(splitLine).not.toHaveBeenCalled(); + expect(send).toHaveBeenCalledOnce(); + expect(getContext().editorTextFocus).toBe(false); + + await ctrl.openFile("/p/a.ts", { focus: false }); + const reopened = slot.mount(host); + ctrl.onMounted(slot); + reopened.focus(); + await vi.waitFor(() => expect(getContext().editorTextFocus).toBe(true)); + reopened.contentDOM.dispatchEvent( + new KeyboardEvent("keydown", { + key: "Enter", + code: "Enter", + metaKey: true, + bubbles: true, + cancelable: true, + }), + ); + expect(splitLine).toHaveBeenCalledOnce(); + } finally { + uninstall(); + dispose(); + slot.unmount(); + host.remove(); + } + }); + it("updates the caret status synchronously on programmatic selection changes", async () => { ctrl = new EditorController(createMemoryBackend({ "/p/a.ts": "one\ntwo\nthree\n" })); ctrl.start(); diff --git a/src/editor/core/controller.ts b/src/editor/core/controller.ts index 0ace3873..39e758f6 100644 --- a/src/editor/core/controller.ts +++ b/src/editor/core/controller.ts @@ -713,6 +713,7 @@ export class EditorController implements SlotHost { Object.values(s.panes).some((p) => p.tabs.length > 0), ); setContext("editorSplit", paneIds(s).length > 1); + setContext("editorTextFocus", !!slot?.view?.hasFocus); setContext("textEditorActive", !!slot); }