mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-07 12:21:24 +00:00
TUI: dedupe duplicate backspace events in input
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { getSlashCommands, parseCommand } from "./commands.js";
|
||||
import {
|
||||
createBackspaceDeduper,
|
||||
resolveFinalAssistantText,
|
||||
resolveGatewayDisconnectState,
|
||||
resolveTuiSessionKey,
|
||||
@@ -87,3 +88,35 @@ describe("resolveGatewayDisconnectState", () => {
|
||||
expect(state.pairingHint).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("createBackspaceDeduper", () => {
|
||||
it("suppresses duplicate backspace events within the dedupe window", () => {
|
||||
let now = 1000;
|
||||
const dedupe = createBackspaceDeduper({
|
||||
dedupeWindowMs: 8,
|
||||
now: () => now,
|
||||
});
|
||||
|
||||
expect(dedupe("\x7f")).toBe("\x7f");
|
||||
now += 1;
|
||||
expect(dedupe("\x08")).toBe("");
|
||||
});
|
||||
|
||||
it("preserves backspace events outside the dedupe window", () => {
|
||||
let now = 1000;
|
||||
const dedupe = createBackspaceDeduper({
|
||||
dedupeWindowMs: 8,
|
||||
now: () => now,
|
||||
});
|
||||
|
||||
expect(dedupe("\x7f")).toBe("\x7f");
|
||||
now += 10;
|
||||
expect(dedupe("\x7f")).toBe("\x7f");
|
||||
});
|
||||
|
||||
it("never suppresses non-backspace keys", () => {
|
||||
const dedupe = createBackspaceDeduper();
|
||||
expect(dedupe("a")).toBe("a");
|
||||
expect(dedupe("\x1b[A")).toBe("\x1b[A");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
CombinedAutocompleteProvider,
|
||||
Container,
|
||||
Key,
|
||||
Loader,
|
||||
matchesKey,
|
||||
ProcessTerminal,
|
||||
Text,
|
||||
TUI,
|
||||
@@ -215,6 +217,24 @@ export function resolveGatewayDisconnectState(reason?: string): {
|
||||
};
|
||||
}
|
||||
|
||||
export function createBackspaceDeduper(params?: { dedupeWindowMs?: number; now?: () => number }) {
|
||||
const dedupeWindowMs = Math.max(0, Math.floor(params?.dedupeWindowMs ?? 8));
|
||||
const now = params?.now ?? (() => Date.now());
|
||||
let lastBackspaceAt = -1;
|
||||
|
||||
return (data: string): string => {
|
||||
if (!matchesKey(data, Key.backspace)) {
|
||||
return data;
|
||||
}
|
||||
const ts = now();
|
||||
if (lastBackspaceAt >= 0 && ts - lastBackspaceAt <= dedupeWindowMs) {
|
||||
return "";
|
||||
}
|
||||
lastBackspaceAt = ts;
|
||||
return data;
|
||||
};
|
||||
}
|
||||
|
||||
export async function runTui(opts: TuiOptions) {
|
||||
const config = loadConfig();
|
||||
const initialSessionInput = (opts.session ?? "").trim();
|
||||
@@ -395,6 +415,14 @@ export async function runTui(opts: TuiOptions) {
|
||||
});
|
||||
|
||||
const tui = new TUI(new ProcessTerminal());
|
||||
const dedupeBackspace = createBackspaceDeduper();
|
||||
tui.addInputListener((data) => {
|
||||
const next = dedupeBackspace(data);
|
||||
if (next.length === 0) {
|
||||
return { consume: true };
|
||||
}
|
||||
return { data: next };
|
||||
});
|
||||
const header = new Text("", 1, 0);
|
||||
const statusContainer = new Container();
|
||||
const footer = new Text("", 1, 0);
|
||||
|
||||
Reference in New Issue
Block a user