🐛 fix(shortcuts): 迁移侧边栏搜索默认快捷键

- 将旧版 Meta/Ctrl+F 一次性迁移为各平台 Meta/Ctrl+K
- 升级持久化版本并避免启动刷新重新读回旧配置
- 保留禁用状态、自定义组合及版本 18 后的 F 配置
- 补充 macOS、Windows/Linux 与旧结构回归测试
This commit is contained in:
Syngnat
2026-07-22 16:55:44 +08:00
parent 8ca2c23d18
commit c067ad8a62
4 changed files with 159 additions and 6 deletions

View File

@@ -113,7 +113,7 @@ describe('store appearance persistence', () => {
expect(useStore.getState().appearance.uiVersion).toBe('v2');
const persisted = JSON.parse(storage.getItem('lite-db-storage') || '{}');
expect(persisted.version).toBe(17);
expect(persisted.version).toBe(18);
expect(persisted.state.appearance.uiVersion).toBe('v2');
});
@@ -1255,7 +1255,7 @@ describe('store appearance persistence', () => {
)).toEqual(legacyTag?.childOrder);
const persisted = JSON.parse(storage.getItem('lite-db-storage') || '{}');
expect(persisted.version).toBe(17);
expect(persisted.version).toBe(18);
expect(persisted.state.connectionTags[0].childOrder).toEqual([
'connection:conn-a',
'connection:conn-b',
@@ -3038,6 +3038,66 @@ describe('store appearance persistence', () => {
});
});
it('migrates legacy sidebar search defaults to K only before storage version 18', async () => {
storage.setItem('lite-db-storage', JSON.stringify({
state: {
shortcutOptions: {
focusSidebarSearch: {
mac: { combo: 'Meta+F', enabled: false },
windows: { combo: 'Ctrl+F', enabled: true },
},
},
},
version: 17,
}));
const migrated = await importStore();
expect(migrated.useStore.getState().shortcutOptions.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+K', enabled: false },
windows: { combo: 'Ctrl+K', enabled: true },
});
expect(JSON.parse(storage.getItem('lite-db-storage') || '{}').version).toBe(18);
storage.setItem('lite-db-storage', JSON.stringify({
state: {
shortcutOptions: {
focusSidebarSearch: {
mac: { combo: 'Meta+F', enabled: true },
windows: { combo: 'Ctrl+F', enabled: false },
},
},
},
version: 18,
}));
vi.resetModules();
const current = await importStore();
expect(current.useStore.getState().shortcutOptions.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+F', enabled: true },
windows: { combo: 'Ctrl+F', enabled: false },
});
});
it('does not restore legacy sidebar search defaults during an early startup refresh', async () => {
const { useStore } = await importStore();
storage.setItem('lite-db-storage', JSON.stringify({
state: {
shortcutOptions: {
focusSidebarSearch: { combo: 'Ctrl+F', enabled: true },
},
},
version: 17,
}));
useStore.getState().replaceConnections([]);
const persisted = JSON.parse(storage.getItem('lite-db-storage') || '{}');
expect(persisted.state.shortcutOptions.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+K', enabled: true },
windows: { combo: 'Ctrl+K', enabled: true },
});
});
it('does not overwrite recorded AI chat send shortcut during startup config refresh', async () => {
const { useStore } = await importStore();
useStore.getState().updateShortcut('sendAIChatMessage', {

View File

@@ -25,6 +25,7 @@ import {
DEFAULT_SHORTCUT_OPTIONS,
cloneShortcutOptions,
getShortcutPlatform,
migrateLegacySidebarSearchShortcutOptions,
sanitizeShortcutOptions,
type ShortcutPlatformBinding,
type ShortcutPlatform,
@@ -257,8 +258,9 @@ const MIN_KEEPALIVE_INTERVAL_MINUTES = 1;
const MAX_KEEPALIVE_INTERVAL_MINUTES = 1440;
const DEFAULT_DIAGNOSTIC_TIMEOUT_SECONDS = 15;
const MAX_DIAGNOSTIC_TIMEOUT_SECONDS = 300;
const PERSIST_VERSION = 17;
const PERSIST_VERSION = 18;
const UI_VERSION_V2_MIGRATION_VERSION = 14;
const SIDEBAR_SEARCH_SHORTCUT_MIGRATION_VERSION = 18;
const PERSIST_STORAGE_KEY = "lite-db-storage";
const PERSIST_WRITE_DEBOUNCE_MS = 160;
const MAX_PERSISTED_QUERY_TABS = 20;
@@ -3101,6 +3103,15 @@ const unwrapPersistedAppState = (
let shortcutOptionsExplicitlySet = false;
const sanitizePersistedShortcutOptions = (
value: unknown,
version: number,
): ShortcutOptions => (
version < SIDEBAR_SEARCH_SHORTCUT_MIGRATION_VERSION
? migrateLegacySidebarSearchShortcutOptions(value)
: sanitizeShortcutOptions(value)
);
const readPersistedShortcutOptions = (): ShortcutOptions | null => {
if (typeof localStorage === "undefined") {
return null;
@@ -3110,11 +3121,13 @@ const readPersistedShortcutOptions = (): ShortcutOptions | null => {
if (!payload) {
return null;
}
const state = unwrapPersistedAppState(JSON.parse(payload));
const raw = JSON.parse(payload) as Record<string, unknown>;
const state = unwrapPersistedAppState(raw);
if (state.shortcutOptions === undefined) {
return null;
}
return sanitizeShortcutOptions(state.shortcutOptions);
const version = typeof raw.version === "number" ? raw.version : 0;
return sanitizePersistedShortcutOptions(state.shortcutOptions, version);
} catch {
return null;
}
@@ -5722,8 +5735,9 @@ export const useStore = create<AppState>()(
sanitizeDataEditTransactionOptions(state.dataEditTransactionOptions);
nextState.sqlEditorTransactionOptions =
sanitizeSqlEditorTransactionOptions(state.sqlEditorTransactionOptions);
nextState.shortcutOptions = sanitizeShortcutOptions(
nextState.shortcutOptions = sanitizePersistedShortcutOptions(
state.shortcutOptions,
version,
);
nextState.sqlLogs = sanitizeRuntimeSqlLogs(state.sqlLogs);
nextState.tableExportHistories = sanitizeTableExportHistories(

View File

@@ -13,6 +13,7 @@ import {
RESERVED_SHORTCUTS,
comboToMonacoKeyBinding,
eventToShortcut,
getShortcutPlatform,
getPrimaryShortcutDisplayLabel,
getShortcutDisplayLabel,
getShortcutPrimaryModifierDisplayLabel,
@@ -22,6 +23,7 @@ import {
isImeComposingKeyEvent,
isShortcutMatch,
isShortcutPhysicalMatch,
migrateLegacySidebarSearchShortcutOptions,
resolveShortcutBinding,
resolveShortcutDisplay,
setGlobalShortcutCaptureActive,
@@ -578,6 +580,13 @@ describe('shortcut defaults', () => {
mac: { combo: 'Meta+Shift+H', enabled: true },
windows: { combo: 'Ctrl+H', enabled: true },
});
expect(DEFAULT_SHORTCUT_OPTIONS.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+K', enabled: true },
windows: { combo: 'Ctrl+K', enabled: true },
});
expect(getShortcutPlatform(true)).toBe('mac');
expect(getShortcutPlatform(false)).toBe('windows');
expect(getShortcutPlatform()).toBe('windows');
});
it('registers connection and AI panel actions as real shortcuts', () => {
@@ -609,6 +618,53 @@ describe('shortcut defaults', () => {
});
});
it('migrates legacy sidebar search defaults by platform while preserving enabled state', () => {
const options = migrateLegacySidebarSearchShortcutOptions({
focusSidebarSearch: {
mac: { combo: 'Meta+F', enabled: false },
windows: { combo: 'Ctrl+F', enabled: true },
},
});
expect(options.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+K', enabled: false },
windows: { combo: 'Ctrl+K', enabled: true },
});
});
it('migrates the pre-platform Ctrl+F binding without changing other custom shortcuts', () => {
const options = migrateLegacySidebarSearchShortcutOptions({
focusSidebarSearch: { combo: 'Ctrl+F', enabled: false },
toggleTheme: {
mac: { combo: 'Meta+Shift+T', enabled: true },
windows: { combo: 'Ctrl+Shift+T', enabled: true },
},
});
expect(options.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+K', enabled: false },
windows: { combo: 'Ctrl+K', enabled: false },
});
expect(options.toggleTheme).toEqual({
mac: { combo: 'Meta+Shift+T', enabled: true },
windows: { combo: 'Ctrl+Shift+T', enabled: true },
});
});
it('keeps non-default sidebar search shortcuts unchanged during legacy migration', () => {
const options = migrateLegacySidebarSearchShortcutOptions({
focusSidebarSearch: {
mac: { combo: 'Meta+P', enabled: true },
windows: { combo: 'Ctrl+P', enabled: false },
},
});
expect(options.focusSidebarSearch).toEqual({
mac: { combo: 'Meta+P', enabled: true },
windows: { combo: 'Ctrl+P', enabled: false },
});
});
it('keeps close active tab enabled for new and empty shortcut settings', () => {
expect(sanitizeShortcutOptions(undefined).closeActiveTab).toEqual(DEFAULT_SHORTCUT_OPTIONS.closeActiveTab);
expect(sanitizeShortcutOptions({}).closeActiveTab).toEqual(DEFAULT_SHORTCUT_OPTIONS.closeActiveTab);

View File

@@ -807,6 +807,29 @@ export const sanitizeShortcutOptions = (value: unknown): ShortcutOptions => {
return defaults;
};
const LEGACY_SIDEBAR_SEARCH_DEFAULTS: Record<ShortcutPlatform, readonly string[]> = {
// The pre-platform schema stored one Ctrl+F binding and copied it into the mac slot.
mac: ['Meta+F', 'Ctrl+F'],
windows: ['Ctrl+F'],
};
export const migrateLegacySidebarSearchShortcutOptions = (value: unknown): ShortcutOptions => {
const options = sanitizeShortcutOptions(value);
(['mac', 'windows'] as const).forEach((platform) => {
const binding = options.focusSidebarSearch[platform];
if (!LEGACY_SIDEBAR_SEARCH_DEFAULTS[platform].includes(normalizeShortcutCombo(binding.combo))) {
return;
}
options.focusSidebarSearch[platform] = {
...binding,
combo: DEFAULT_SHORTCUT_OPTIONS.focusSidebarSearch[platform].combo,
};
});
return options;
};
export const resolveShortcutBinding = (
options: Partial<ShortcutOptions> | null | undefined,
action: ShortcutAction,