mirror of https://github.com/openclaw/openclaw.git
fix(tui): stop hijacking j/k in model search
This commit is contained in:
parent
dc64a86eb8
commit
64da916590
|
|
@ -26,6 +26,7 @@ Docs: https://docs.openclaw.ai
|
|||
- Memory/FTS: add configurable trigram tokenization plus short-CJK substring fallback so memory search can find Chinese, Japanese, and Korean text without breaking mixed long-and-short queries. Thanks @carrotRakko.
|
||||
- Hooks/config: accept runtime channel plugin ids in `hooks.mappings[].channel` (for example `feishu`) instead of rejecting non-core channels during config validation. (#56226) Thanks @AiKrai001.
|
||||
- TUI/chat: keep optimistic outbound user messages visible during active runs by deferring local-run binding until the first gateway chat event reveals the real run id, preventing premature history reloads from wiping pending local sends. (#54722) Thanks @seanturner001.
|
||||
- TUI/model picker: keep searchable `/model` and `/models` input mode from hijacking `j`/`k` as navigation keys, and harden width bounds under `m`-filtered model lists so search no longer crashes on long rows. (#30156) Thanks @briannicholls.
|
||||
|
||||
## 2026.3.28
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,34 @@ describe("SearchableSelectList", () => {
|
|||
}
|
||||
});
|
||||
|
||||
it("keeps model-search rows within width when filtering by m", () => {
|
||||
const items = [
|
||||
{ value: "minimax-cn/MiniMax-M2", label: "minimax-cn/MiniMax-M2", description: "MiniMax M2" },
|
||||
{
|
||||
value: "minimax-cn/MiniMax-M2.1",
|
||||
label: "minimax-cn/MiniMax-M2.1",
|
||||
description: "MiniMax M2.1",
|
||||
},
|
||||
{
|
||||
value: "mistral/codestral-latest",
|
||||
label: "mistral/codestral-latest",
|
||||
description: "Codestral",
|
||||
},
|
||||
{
|
||||
value: "mistral/devstral-medium-latest",
|
||||
label: "mistral/devstral-medium-latest",
|
||||
description: "Devstral Medium",
|
||||
},
|
||||
];
|
||||
const list = new SearchableSelectList(items, 9, ansiHighlightTheme);
|
||||
typeInput(list, "m");
|
||||
|
||||
const width = 209;
|
||||
for (const line of list.render(width)) {
|
||||
expect(visibleWidth(line)).toBeLessThanOrEqual(width);
|
||||
}
|
||||
});
|
||||
|
||||
it("ignores ANSI escape codes in search matching", () => {
|
||||
const items = [
|
||||
{ value: "styled", label: "\u001b[32mopenai/gpt-4\u001b[0m", description: "Styled label" },
|
||||
|
|
@ -266,6 +294,24 @@ describe("SearchableSelectList", () => {
|
|||
expect(list.getSelectedItem()?.value).toBe("anthropic/claude-3-sonnet");
|
||||
});
|
||||
|
||||
it("types j and k into search input instead of intercepting as vim navigation", () => {
|
||||
const items = [
|
||||
{ value: "alpha", label: "alpha" },
|
||||
{ value: "kilo", label: "kilo" },
|
||||
{ value: "juliet", label: "juliet" },
|
||||
];
|
||||
|
||||
const jList = new SearchableSelectList(items, 5, mockTheme);
|
||||
jList.handleInput("j");
|
||||
expect(jList.getSelectedItem()?.value).toBe("juliet");
|
||||
expect(stripAnsi(jList.render(80)[0] ?? "")).toContain("j");
|
||||
|
||||
const kList = new SearchableSelectList(items, 5, mockTheme);
|
||||
kList.handleInput("k");
|
||||
expect(kList.getSelectedItem()?.value).toBe("kilo");
|
||||
expect(stripAnsi(kList.render(80)[0] ?? "")).toContain("k");
|
||||
});
|
||||
|
||||
it("calls onSelect when enter is pressed", () => {
|
||||
const list = new SearchableSelectList(testItems, 5, mockTheme);
|
||||
let selectedValue: string | undefined;
|
||||
|
|
|
|||
|
|
@ -330,24 +330,14 @@ export class SearchableSelectList implements Component {
|
|||
return;
|
||||
}
|
||||
|
||||
const allowVimNav = !this.searchInput.getValue().trim();
|
||||
|
||||
// Navigation keys
|
||||
if (
|
||||
matchesKey(keyData, "up") ||
|
||||
matchesKey(keyData, "ctrl+p") ||
|
||||
(allowVimNav && keyData === "k")
|
||||
) {
|
||||
if (matchesKey(keyData, "up") || matchesKey(keyData, "ctrl+p")) {
|
||||
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
|
||||
this.notifySelectionChange();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
matchesKey(keyData, "down") ||
|
||||
matchesKey(keyData, "ctrl+n") ||
|
||||
(allowVimNav && keyData === "j")
|
||||
) {
|
||||
if (matchesKey(keyData, "down") || matchesKey(keyData, "ctrl+n")) {
|
||||
this.selectedIndex = Math.min(this.filteredItems.length - 1, this.selectedIndex + 1);
|
||||
this.notifySelectionChange();
|
||||
return;
|
||||
|
|
|
|||
Loading…
Reference in New Issue