feat: fix Redis key search visibility

This commit is contained in:
onenewcode
2026-07-02 15:13:11 +08:00
committed by GitHub
parent b61ed7c255
commit 1ef757e88f
@@ -813,6 +813,7 @@ function typeColor(type: string): string {
}
let searchTimer: ReturnType<typeof setTimeout> | null = null;
let hasAutoFocusedSearch = false;
function onSearchInput() {
if (searchTimer) clearTimeout(searchTimer);
@@ -834,6 +835,15 @@ function getSearchInput(): HTMLInputElement | null {
return rootRef.value?.querySelector<HTMLInputElement>("[data-redis-search-input]") ?? null;
}
async function autofocusSearchOnce() {
if (hasAutoFocusedSearch) return;
await nextTick();
const input = getSearchInput();
if (!input) return;
hasAutoFocusedSearch = true;
input.focus({ preventScroll: true });
}
function getCommandInput(): HTMLInputElement | null {
return rootRef.value?.querySelector<HTMLInputElement>("[data-redis-command-input]") ?? null;
}
@@ -938,6 +948,7 @@ function onCommandInputKeydown(event: KeyboardEvent) {
onMounted(async () => {
resumeRedisBrowserBackgroundWork();
void autofocusSearchOnce();
try {
await connectionStore.ensureConnected(props.connectionId);
} catch (e) {
@@ -948,6 +959,7 @@ onMounted(async () => {
onActivated(async () => {
resumeRedisBrowserBackgroundWork();
void autofocusSearchOnce();
// Ensure the connection is still alive after reactivation (e.g. tab switch).
// If keys failed to load previously (empty list), retry loading.
try {
@@ -981,33 +993,57 @@ defineExpose({ focusSearch });
<Pane :size="36" :min-size="24">
<div class="relative h-full flex flex-col overflow-hidden">
<!-- Toolbar -->
<div class="h-9 flex items-center gap-1 px-2 border-b shrink-0">
<Search class="w-3.5 h-3.5 text-muted-foreground shrink-0" />
<div class="h-6 flex rounded-md border bg-muted/30 p-0.5 shrink-0" role="group">
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'key' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('key')">
{{ t("redis.searchByKey") }}
</button>
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'value' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('value')">
{{ t("redis.searchByValue") }}
</button>
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'all' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('all')">
{{ t("redis.searchByAll") }}
</button>
<div class="border-b px-2 py-2 shrink-0">
<div class="flex flex-wrap items-start gap-1.5">
<div class="flex min-w-0 flex-1 flex-wrap rounded-md border bg-muted/30 p-0.5" role="group">
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'key' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('key')">
{{ t("redis.searchByKey") }}
</button>
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'value' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('value')">
{{ t("redis.searchByValue") }}
</button>
<button type="button" class="h-5 px-2 text-xs rounded-sm transition-colors" :class="searchMode === 'all' ? 'bg-background text-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground'" @click="setSearchMode('all')">
{{ t("redis.searchByAll") }}
</button>
</div>
<div class="ml-auto flex min-w-0 flex-wrap items-center justify-end gap-1">
<span class="min-w-0 max-w-full truncate text-xs text-muted-foreground" :title="keyCountText">{{ keyCountText }}</span>
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 shrink-0 text-xs text-destructive" @click="requestBatchDelete"> <Trash2 class="w-3 h-3 mr-1" />{{ checkedKeys.size }} </Button>
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" @click="loadKeys">
<Loader2 v-if="loading" class="h-3 w-3 animate-spin" />
<RefreshCw v-else class="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" :title="t('redis.createKey')" @click="openCreateKeyDialog">
<Plus class="h-3 w-3" />
</Button>
</div>
</div>
<div class="mt-2 flex min-w-0 flex-wrap items-center gap-1.5">
<div class="relative min-w-[120px] flex-1 basis-[180px]">
<Search class="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/80" />
<Input
v-model="searchPattern"
data-redis-search-input
class="h-8 border-border/70 bg-background pl-8 pr-3 text-xs shadow-sm caret-primary placeholder:text-muted-foreground/80 focus-visible:border-primary/60 focus-visible:ring-2 focus-visible:ring-primary/20"
:placeholder="searchPlaceholder"
@input="onSearchInput"
@keydown="onSearchKeydown"
/>
</div>
<Button
v-if="searchMode === 'key'"
variant="ghost"
size="sm"
class="h-8 max-w-full shrink-0 px-2 text-xs"
:class="fuzzyKeySearch ? 'bg-accent text-accent-foreground' : 'border border-dashed border-border/70 text-muted-foreground hover:text-foreground'"
:title="t('redis.fuzzyMatchTitle')"
:aria-pressed="fuzzyKeySearch"
@click="toggleFuzzyKeySearch"
>
<Asterisk class="h-3 w-3 mr-1" />
{{ t("redis.fuzzyMatch") }}
</Button>
</div>
<Input v-model="searchPattern" data-redis-search-input class="h-6 text-xs border-0 shadow-none focus-visible:ring-0" :placeholder="searchPlaceholder" @input="onSearchInput" @keydown="onSearchKeydown" />
<Button v-if="searchMode === 'key'" variant="ghost" size="sm" class="h-6 shrink-0 px-2 text-xs" :class="fuzzyKeySearch ? 'bg-accent text-accent-foreground' : 'text-muted-foreground'" :title="t('redis.fuzzyMatchTitle')" :aria-pressed="fuzzyKeySearch" @click="toggleFuzzyKeySearch">
<Asterisk class="h-3 w-3 mr-1" />
{{ t("redis.fuzzyMatch") }}
</Button>
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" @click="loadKeys">
<Loader2 v-if="loading" class="h-3 w-3 animate-spin" />
<RefreshCw v-else class="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" class="h-6 w-6 shrink-0" :title="t('redis.createKey')" @click="openCreateKeyDialog">
<Plus class="h-3 w-3" />
</Button>
<span class="text-xs text-muted-foreground shrink-0 ml-1">{{ keyCountText }}</span>
<Button v-if="checkedKeys.size > 0" variant="ghost" size="sm" class="h-6 text-xs text-destructive shrink-0 ml-1" @click="requestBatchDelete"> <Trash2 class="w-3 h-3 mr-1" />{{ checkedKeys.size }} </Button>
</div>
<div v-if="flatKeys.length === 0 && !loading" class="flex-1 flex flex-col items-center justify-center text-muted-foreground text-xs p-4 text-center">