mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-11 09:13:36 +08:00
🐛 fix(shortcuts): 迁移侧边栏搜索默认快捷键
- 将旧版 Meta/Ctrl+F 一次性迁移为各平台 Meta/Ctrl+K - 升级持久化版本并避免启动刷新重新读回旧配置 - 保留禁用状态、自定义组合及版本 18 后的 F 配置 - 补充 macOS、Windows/Linux 与旧结构回归测试
This commit is contained in:
@@ -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', {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user