fix(coding-agent): spawn the WSL text clipboard read async and route the text fallback by focus

Address review notes on #2248:

- readTextFromClipboard's WSL branch now mirrors readImageViaPowerShell
  (Bun.spawn + kill timer sharing POWERSHELL_TIMEOUT_MS) instead of
  execSync, so a cold powershell.exe start cannot block the TUI event
  loop on the first smart-paste miss.
- The smart-paste text fallback routes through ui.getFocused() +
  hasPasteText like the enhanced-paste text path, so the payload lands
  in focused modal Input prompts instead of the hidden main editor
  (#2127 contract). Covered by a new routing test.
This commit is contained in:
HugoAir Dev
2026-06-10 17:59:57 +09:00
parent 765b972a00
commit 5cd9c016d1
3 changed files with 72 additions and 17 deletions
@@ -10,14 +10,14 @@ import { describe, expect, it, vi } from "bun:test";
import { InputController } from "@oh-my-pi/pi-coding-agent/modes/controllers/input-controller";
import type { InteractiveModeContext } from "@oh-my-pi/pi-coding-agent/modes/types";
function createContext() {
function createContext(options?: { focused?: { pasteText(text: string): void } }) {
const pasteText = vi.fn();
const insertText = vi.fn();
const requestRender = vi.fn();
const showStatus = vi.fn();
const ctx = {
editor: { pasteText, insertText } as unknown as InteractiveModeContext["editor"],
ui: { requestRender } as unknown as InteractiveModeContext["ui"],
ui: { requestRender, getFocused: () => options?.focused ?? null } as unknown as InteractiveModeContext["ui"],
showStatus,
} as unknown as InteractiveModeContext;
return { ctx, spies: { pasteText, insertText, requestRender, showStatus } };
@@ -58,6 +58,21 @@ describe("InputController.handleImagePaste smart-paste fallback", () => {
expect(spies.showStatus).not.toHaveBeenCalled();
});
it("routes the text fallback to a focused paste-capable component (#2127 contract)", async () => {
const focusedPasteText = vi.fn();
const { ctx, spies } = createContext({ focused: { pasteText: focusedPasteText } });
const controller = new InputController(ctx, {
readImage: async () => null,
readText: async () => "api-key-123",
});
const result = await controller.handleImagePaste();
expect(result).toBe(true);
expect(focusedPasteText).toHaveBeenCalledWith("api-key-123");
expect(spies.pasteText).not.toHaveBeenCalled();
});
it("reports an empty clipboard when neither image nor text is available", async () => {
const { ctx, spies } = createContext();
const controller = new InputController(ctx, {