fix(tui): preserve input typed after submit

This commit is contained in:
chengyongru
2026-09-01 16:21:18 +08:00
committed by chengyongru
parent 22cbfc2fb5
commit eebe3ca292
2 changed files with 39 additions and 7 deletions
+17
View File
@@ -253,6 +253,23 @@ describe("NanobotTui layout", () => {
expect(sent).toEqual(["你好"])
})
test("keeps input typed immediately after Enter in the next draft", async () => {
const sent: string[] = []
setup = await createRenderer({ width: 72, height: 20, screenMode: "alternate-screen" })
const app = mount(setup, sent)
app.accept({ event: "attached", chat_id: "chat" })
await Bun.sleep(1)
const composer = (app as unknown as { composer: TextareaRenderable }).composer
composer.setText("first")
setup.mockInput.pressEnter()
for (const key of "next") setup.mockInput.pressKey(key)
await waitUntil(() => sent.length > 0)
expect(sent).toEqual(["first"])
expect(composer.plainText).toBe("next")
})
test("inserts newlines with Shift+Enter and the universal Ctrl+J fallback", async () => {
const sent: string[] = []
setup = await createRenderer({
+22 -7
View File
@@ -733,7 +733,10 @@ export class NanobotTui {
// IMEs may commit their final composed glyph after Enter. Matching the
// OpenCode/OpenTUI integration, defer twice before reading plainText.
onSubmit: () => this.deferSubmit(),
onPaste: (event) => this.handlePaste(event),
onPaste: (event) => {
this.flushSubmit()
if (!this.composer.isDestroyed) this.handlePaste(event)
},
})
this.status = new TextRenderable(renderer, {
id: "nanobot-tui-status",
@@ -855,12 +858,15 @@ export class NanobotTui {
if (this.submitPending) return
this.submitPending = true
const generation = ++this.submitGeneration
setTimeout(() => setTimeout(() => {
if (generation !== this.submitGeneration) return
this.submitPending = false
if (this.composer.isDestroyed) return
this.submit()
}, 0), 0)
setTimeout(() => setTimeout(() => this.flushSubmit(generation), 0), 0)
}
private flushSubmit(generation = this.submitGeneration): void {
if (!this.submitPending || generation !== this.submitGeneration) return
this.submitPending = false
this.submitGeneration += 1
if (this.composer.isDestroyed) return
this.submit()
}
private submit(): void {
@@ -1575,6 +1581,15 @@ export class NanobotTui {
}
private handleKey = (key: KeyEvent): void => {
// The app receives keypresses before the focused Textarea. Seal the pending
// submission first so this key is inserted into the next draft.
if (this.submitPending) {
this.flushSubmit()
if (this.quitting || this.composer.isDestroyed) {
key.preventDefault()
return
}
}
if (this.diffViewer.visible) {
if (key.ctrl && key.name === "c") {
const selected = this.renderer.getSelection()?.getSelectedText()