Skip to content

Commit 14c3c6a

Browse files
committed
feat(tui): add ctrl+r prompt history search dialog
Rebind ctrl+r from session rename to a new prompt history search dialog. The dialog shows deduplicated history entries most-recent-first with fuzzy search filtering. Selecting an entry restores the full prompt state including text, mode, and file/agent parts. Also available as the /history slash command. Session rename remains accessible via /rename.
1 parent 3f6edc3 commit 14c3c6a

4 files changed

Lines changed: 71 additions & 1 deletion

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { DialogSelect, type DialogSelectOption } from "../ui/dialog-select"
2+
import { createMemo } from "solid-js"
3+
import { useDialog } from "../ui/dialog"
4+
import type { PromptInfo } from "../prompt/history"
5+
6+
export type DialogPromptHistoryProps = {
7+
history: PromptInfo[]
8+
onSelect: (item: PromptInfo) => void
9+
}
10+
11+
export function DialogPromptHistory(props: DialogPromptHistoryProps) {
12+
const dialog = useDialog()
13+
dialog.setSize("large")
14+
15+
const options = createMemo<DialogSelectOption<PromptInfo>[]>(() => {
16+
// Show most recent first, deduplicate by input text
17+
const seen = new Set<string>()
18+
const unique: PromptInfo[] = []
19+
for (let i = props.history.length - 1; i >= 0; i--) {
20+
const item = props.history[i]
21+
if (!item || seen.has(item.input)) continue
22+
seen.add(item.input)
23+
unique.push(item)
24+
}
25+
return unique.map((item) => ({
26+
title: item.input.replace(/\s+/g, " ").trim(),
27+
value: item,
28+
category: "History",
29+
onSelect: () => {
30+
props.onSelect(item)
31+
dialog.clear()
32+
},
33+
}))
34+
})
35+
36+
return (
37+
<DialogSelect
38+
title="Prompt History"
39+
placeholder="Search history..."
40+
options={options()}
41+
/>
42+
)
43+
}

packages/tui/src/component/prompt/index.tsx

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import { DialogAlert } from "../../ui/dialog-alert"
4848
import { useToast } from "../../ui/toast"
4949
import { useKV } from "../../context/kv"
5050
import { createFadeIn } from "../../util/signal"
51+
import { DialogPromptHistory } from "../dialog-prompt-history"
5152
import { DialogSkill } from "../dialog-skill"
5253
import { DialogWorkspaceUnavailable } from "../dialog-workspace-unavailable"
5354
import { DialogBtw } from "../dialog-btw"
@@ -532,6 +533,26 @@ export function Prompt(props: PromptProps) {
532533
))
533534
},
534535
},
536+
{
537+
title: "Search prompt history",
538+
name: "prompt.history.search",
539+
category: "Prompt",
540+
slashName: "history",
541+
run: () => {
542+
dialog.replace(() => (
543+
<DialogPromptHistory
544+
history={history.list()}
545+
onSelect={(item) => {
546+
input.setText(item.input)
547+
setStore("prompt", item)
548+
setStore("mode", item.mode ?? "normal")
549+
restoreExtmarksFromParts(item.parts)
550+
input.gotoBufferEnd()
551+
}}
552+
/>
553+
))
554+
},
555+
},
535556
{
536557
title: "Warp",
537558
desc: "Change the workspace for the session",
@@ -573,6 +594,7 @@ export function Prompt(props: PromptProps) {
573594
"prompt.stash.pop",
574595
"prompt.stash.list",
575596
"prompt.skills",
597+
"prompt.history.search",
576598
"session.interrupt",
577599
"workspace.set",
578600
"session.move",

packages/tui/src/config/keybind.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export const Definitions = {
9090
session_list: keybind("<leader>l", "List all sessions"),
9191
session_timeline: keybind("<leader>g", "Show session timeline"),
9292
session_fork: keybind("none", "Fork session from message"),
93-
session_rename: keybind("ctrl+r", "Rename session"),
93+
session_rename: keybind("none", "Rename session"),
9494
session_delete: keybind("ctrl+d", "Delete session"),
9595
session_share: keybind("none", "Share current session"),
9696
session_unshare: keybind("none", "Unshare current session"),
@@ -198,6 +198,7 @@ export const Definitions = {
198198
input_select_all: keybind("super+a", "Select all in input"),
199199
history_previous: keybind("up", "Previous history item"),
200200
history_next: keybind("down", "Next history item"),
201+
history_search: keybind("ctrl+r", "Search prompt history"),
201202

202203
"dialog.select.prev": keybind("up,ctrl+p", "Move to previous dialog item"),
203204
"dialog.select.next": keybind("down,ctrl+n", "Move to next dialog item"),
@@ -401,6 +402,7 @@ export const CommandMap = {
401402
input_select_all: "input.select.all",
402403
history_previous: "prompt.history.previous",
403404
history_next: "prompt.history.next",
405+
history_search: "prompt.history.search",
404406
terminal_suspend: "terminal.suspend",
405407
terminal_title_toggle: "terminal.title.toggle",
406408
tips_toggle: "tips.toggle",

packages/tui/src/prompt/history.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ export const { use: usePromptHistory, provider: PromptHistoryProvider } = create
6666
})
6767

6868
return {
69+
list() {
70+
return store.history
71+
},
6972
move(direction: 1 | -1, input: string) {
7073
if (!store.history.length) return undefined
7174
const current = store.history.at(store.index)

0 commit comments

Comments
 (0)