From dbc8668cedfc8336d74d1c167012ba8cbf1ebb23 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Sun, 21 Jun 2026 15:39:55 +0800 Subject: [PATCH] feat(redis): add command history navigation with arrow keys MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Press ↑ to recall previous command, ↓ for next command (terminal-like behavior) - Press ↓ at end of history to clear input - Use readonly instead of disabled to maintain cursor focus and blink state - Semi-transparent visual feedback during command execution --- .../src/components/redis/RedisKeyBrowser.vue | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/apps/desktop/src/components/redis/RedisKeyBrowser.vue b/apps/desktop/src/components/redis/RedisKeyBrowser.vue index 864e34a486..a1ed3a09eb 100644 --- a/apps/desktop/src/components/redis/RedisKeyBrowser.vue +++ b/apps/desktop/src/components/redis/RedisKeyBrowser.vue @@ -82,6 +82,7 @@ const commandText = ref(""); const commandRunning = ref(false); const commandDb = ref(props.db); const commandHistory = ref([]); +const commandHistoryIndex = ref(-1); const activeSidePanel = ref("detail"); const showCreateKeyDialog = ref(false); const creatingKey = ref(false); @@ -692,6 +693,7 @@ async function executeCommand() { if (isRedisClearScreenCommand(command)) { commandHistory.value = []; commandText.value = ""; + commandHistoryIndex.value = -1; scrollCommandTerminalToEnd(); return; } @@ -705,15 +707,18 @@ async function executeCommand() { error: true, }); commandText.value = ""; + commandHistoryIndex.value = -1; return; } if (safety === "confirm") { pendingDanger.value = { kind: "command", command }; showDangerConfirm.value = true; commandText.value = ""; + commandHistoryIndex.value = -1; return; } commandText.value = ""; + commandHistoryIndex.value = -1; await runRedisCommand(command); } @@ -840,6 +845,39 @@ function onCommandAreaClick() { } } +function onCommandInputKeydown(event: KeyboardEvent) { + // 上下键切换历史命令 + if (event.key === "ArrowUp") { + event.preventDefault(); + if (commandHistory.value.length === 0) return; + + if (commandHistoryIndex.value === -1) { + // 首次按上键,从最后一条开始 + commandHistoryIndex.value = commandHistory.value.length - 1; + } else if (commandHistoryIndex.value > 0) { + // 继续往前 + commandHistoryIndex.value--; + } + commandText.value = commandHistory.value[commandHistoryIndex.value].command; + } else if (event.key === "ArrowDown") { + event.preventDefault(); + if (commandHistoryIndex.value === -1) return; + + if (commandHistoryIndex.value < commandHistory.value.length - 1) { + // 往后 + commandHistoryIndex.value++; + commandText.value = commandHistory.value[commandHistoryIndex.value].command; + } else { + // 到达末尾,清空输入 + commandHistoryIndex.value = -1; + commandText.value = ""; + } + } else if (event.key === "Enter") { + event.preventDefault(); + executeCommand(); + } +} + onMounted(() => { resumeRedisBrowserBackgroundWork(); void loadKeys(); @@ -1009,11 +1047,12 @@ defineExpose({ focusSearch }); v-model="commandText" data-redis-command-input class="dbx-editor-font-family min-w-0 flex-1 border-0 bg-transparent p-0 text-[13px] text-slate-200 caret-[#d7ba7d] outline-none placeholder:text-slate-500" - :disabled="commandRunning" + :class="{ 'opacity-50': commandRunning }" + :readonly="commandRunning" autocomplete="off" autocapitalize="off" spellcheck="false" - @keydown.enter.prevent="executeCommand" + @keydown="onCommandInputKeydown" />