feat(redis): limit the sidebar database list

This commit is contained in:
Melody Ruch authored and GitHub committed 2026-09-18 17:33:28 +08:00
1 parent 745c201f06
commit ec356293aa
18 files changed
+265 -22

No files matched your search

@@ -178,6 +178,7 @@ import { formatShortcutDisplay } from "@/lib/editor/shortcutDisplay";
import { COLUMN_NAME_COPY_SEPARATOR_LABELS, COLUMN_NAME_COPY_SEPARATOR_OPTIONS, isColumnNameCopySeparator, type ColumnNameCopySeparator } from "@/lib/dataGrid/dataGridColumnNameCopy";
import { normalizeSidebarHiddenTablePrefixes } from "@/lib/sidebar/sidebarTableNameDisplay";
import { normalizeRedisKeyTemplates } from "@/lib/redis/redisKeyTemplates";
import { REDIS_DATABASE_DISPLAY_LIMIT_MIN, REDIS_DATABASE_DISPLAY_LIMIT_MAX, REDIS_DATABASE_DISPLAY_LIMIT_OPTIONS } from "@/lib/redis/redisDatabaseAlias";
import { currentStatementFrameRangeTo } from "@/lib/sql/currentStatementFrame";
import { currentStatementFrameLayer } from "@/lib/editor/codemirrorCurrentStatementFrameLayer";
import { buildQueryEditorLineNumbersExtension } from "@/lib/editor/queryEditorLineNumbers";
@@ -693,6 +694,7 @@ const editSidebarHiddenTablePrefixes = ref(settingsStore.editorSettings.sidebarH
const editSidebarCopyTableNameSeparator = ref<ColumnNameCopySeparator>(settingsStore.editorSettings.sidebarCopyTableNameSeparator);
const editSidebarCopyTableNameIncludeSchema = ref(settingsStore.editorSettings.sidebarCopyTableNameIncludeSchema);
const editRedisKeyTemplates = ref(normalizeRedisKeyTemplates(settingsStore.editorSettings.redisKeyTemplates).join("\n"));
const editRedisDatabaseDisplayLimit = ref(settingsStore.editorSettings.redisDatabaseDisplayLimit);
const editSidebarObjectInfoMode = ref<SidebarObjectInfoMode>(settingsStore.editorSettings.sidebarObjectInfoMode);
const editSidebarAllowHorizontalScroll = ref(settingsStore.editorSettings.sidebarAllowHorizontalScroll);
const editSidebarShowTooltips = ref(settingsStore.editorSettings.sidebarShowTooltips);
@@ -929,6 +931,7 @@ function currentEditorSettingsDraft(): EditorSettingsDraft {
sidebarCopyTableNameSeparator: editSidebarCopyTableNameSeparator.value,
sidebarCopyTableNameIncludeSchema: editSidebarCopyTableNameIncludeSchema.value,
redisKeyTemplates: normalizeRedisKeyTemplates(editRedisKeyTemplates.value),
redisDatabaseDisplayLimit: editRedisDatabaseDisplayLimit.value,
exportBatchSize: editExportBatchSize.value,
csvQuoteMode: editCsvQuoteMode.value,
globalDateTimeDisplayFormat: editGlobalDateTimeDisplayFormat.value,
@@ -1558,6 +1561,7 @@ function syncEditorSettingsDraftFromStore() {
editSidebarCopyTableNameSeparator.value = settingsStore.editorSettings.sidebarCopyTableNameSeparator;
editSidebarCopyTableNameIncludeSchema.value = settingsStore.editorSettings.sidebarCopyTableNameIncludeSchema;
editRedisKeyTemplates.value = normalizeRedisKeyTemplates(settingsStore.editorSettings.redisKeyTemplates).join("\n");
editRedisDatabaseDisplayLimit.value = settingsStore.editorSettings.redisDatabaseDisplayLimit;
editSidebarObjectInfoMode.value = settingsStore.editorSettings.sidebarObjectInfoMode;
editSidebarAllowHorizontalScroll.value = settingsStore.editorSettings.sidebarAllowHorizontalScroll;
editSidebarShowTooltips.value = settingsStore.editorSettings.sidebarShowTooltips;
@@ -1683,6 +1687,7 @@ const editorSettingsDraftRefs: EditorSettingsDraftRefMap = {
sidebarCopyTableNameSeparator: editSidebarCopyTableNameSeparator,
sidebarCopyTableNameIncludeSchema: editSidebarCopyTableNameIncludeSchema,
redisKeyTemplates: editRedisKeyTemplates,
redisDatabaseDisplayLimit: editRedisDatabaseDisplayLimit,
exportBatchSize: editExportBatchSize,
csvQuoteMode: editCsvQuoteMode,
exportRowLimitEnabled: editExportRowLimitEnabled,
@@ -1951,6 +1956,10 @@ async function persistSettings() {
settingsStore.updateEditorSettings(editorSettingsPatch);
await settingsStore.persistEditorSettings();
editEditorSettingsBase.value = editorSettingsDraftFromSettings(settingsStore.editorSettings);
// updateEditorSettings clamps out-of-range values; reflect the clamped
// result back into the input so an out-of-range draft doesn't keep
// reporting unsaved changes after a successful apply.
editRedisDatabaseDisplayLimit.value = settingsStore.editorSettings.redisDatabaseDisplayLimit;
}
if (pendingBackgroundImageCleanup) {
const cleanupPath = pendingBackgroundImageCleanup;
@@ -2142,6 +2151,7 @@ function resetDefaultsForTab(tab: SettingsCategory) {
editDuckDbWorkerMaxProcesses.value = DEFAULT_DESKTOP_SETTINGS.duckdb_worker_max_processes;
editTableColumnTemplateRows.value = tableColumnTemplateRowsFromSettings(DEFAULT_EDITOR_SETTINGS.tableColumnTemplateFields);
editRedisKeyTemplates.value = normalizeRedisKeyTemplates(DEFAULT_EDITOR_SETTINGS.redisKeyTemplates).join("\n");
editRedisDatabaseDisplayLimit.value = DEFAULT_EDITOR_SETTINGS.redisDatabaseDisplayLimit;
editExportBatchSize.value = DEFAULT_EDITOR_SETTINGS.exportBatchSize;
editCsvQuoteMode.value = DEFAULT_EDITOR_SETTINGS.csvQuoteMode;
editGlobalDateTimeDisplayFormat.value = DEFAULT_EDITOR_SETTINGS.globalDateTimeDisplayFormat;
@@ -2267,6 +2277,7 @@ function resetAllDefaults() {
editSidebarCopyTableNameSeparator.value = DEFAULT_EDITOR_SETTINGS.sidebarCopyTableNameSeparator;
editSidebarCopyTableNameIncludeSchema.value = DEFAULT_EDITOR_SETTINGS.sidebarCopyTableNameIncludeSchema;
editRedisKeyTemplates.value = normalizeRedisKeyTemplates(DEFAULT_EDITOR_SETTINGS.redisKeyTemplates).join("\n");
editRedisDatabaseDisplayLimit.value = DEFAULT_EDITOR_SETTINGS.redisDatabaseDisplayLimit;
editExportBatchSize.value = DEFAULT_EDITOR_SETTINGS.exportBatchSize;
editCsvQuoteMode.value = DEFAULT_EDITOR_SETTINGS.csvQuoteMode;
editGlobalDateTimeDisplayFormat.value = DEFAULT_EDITOR_SETTINGS.globalDateTimeDisplayFormat;
@@ -7716,6 +7727,25 @@ onUnmounted(() => {
{{ t("settings.redisKeyTemplatesDescription") }}
</p>
</div>
<div class="space-y-2">
<Label for="redis-database-display-limit-input">{{ t("settings.redisDatabaseDisplayLimit") }}</Label>
<div class="flex items-center gap-3">
<Input
id="redis-database-display-limit-input"
type="number"
list="redis-database-display-limits"
:min="REDIS_DATABASE_DISPLAY_LIMIT_MIN"
:max="REDIS_DATABASE_DISPLAY_LIMIT_MAX"
step="10"
v-model.number="editRedisDatabaseDisplayLimit"
class="settings-export-number-input h-9 w-28 [&::-webkit-inner-spin-button]:appearance-none"
/>
<datalist id="redis-database-display-limits">
<option v-for="size in REDIS_DATABASE_DISPLAY_LIMIT_OPTIONS" :key="size" :value="size" />
</datalist>
<span class="text-xs text-muted-foreground">{{ t("settings.redisDatabaseDisplayLimitDescription") }}</span>
</div>
</div>
</div>
<Separator />
@@ -0,0 +1,25 @@
import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dialogSource = readFileSync(new URL("../EditorSettingsDialog.vue", import.meta.url), "utf8");
// Regression found while verifying issue #1236 (Redis sidebar database list
// limit): updateEditorSettings() silently clamps an out-of-range
// redisDatabaseDisplayLimit to [MIN, MAX], but persistSettings() only
// re-synced the dirty-check baseline (editEditorSettingsBase), not the input's
// own v-model ref. Typing a below-minimum value and clicking Apply left the
// draft (the unclamped value) permanently out of sync with the saved
// (clamped) value, so the dialog kept reporting unsaved changes forever after
// a successful apply.
describe("EditorSettingsDialog Redis database display limit clamp resync", () => {
it("reflects the clamped value back into the input after a successful apply", () => {
const block = dialogSource.slice(dialogSource.indexOf("async function persistSettings()"), dialogSource.indexOf("function applySettingsErrorToast"));
expect(block).toContain("editEditorSettingsBase.value = editorSettingsDraftFromSettings(settingsStore.editorSettings);");
expect(block).toContain("editRedisDatabaseDisplayLimit.value = settingsStore.editorSettings.redisDatabaseDisplayLimit;");
// The resync must happen after the clamp-and-persist step, inside the same
// "there is a pending editor-settings patch" branch, not unconditionally.
const clampResyncIndex = block.indexOf("editRedisDatabaseDisplayLimit.value = settingsStore.editorSettings.redisDatabaseDisplayLimit;");
const persistIndex = block.indexOf("await settingsStore.persistEditorSettings();");
expect(clampResyncIndex).toBeGreaterThan(persistIndex);
});
});