fix(settings): normalize shortcut edits to the persisted form
This commit is contained in:
14 files changed
+174
-4
No files matched your search
@@ -174,7 +174,7 @@ import {
|
||||
type WebDavConfig,
|
||||
} from "@/lib/backend/api";
|
||||
import { eventToModifierOnlyShortcut, eventToShortcut } from "@/lib/editor/keyboardShortcuts";
|
||||
import { SHORTCUT_DEFINITIONS, countShortcutConflictPairs, findCrossScopeShortcutConflicts, findShortcutConflict, isReservedShortcut, normalizeShortcutSettings, type ShortcutActionId, type ShortcutDefinition, type ShortcutScope } from "@/lib/editor/shortcutRegistry";
|
||||
import { SHORTCUT_DEFINITIONS, countShortcutConflictPairs, findCrossScopeShortcutConflicts, findShortcutConflict, isReservedShortcut, normalizeShortcutSettings, resolveCapturedShortcutEdit, type ShortcutActionId, type ShortcutDefinition, type ShortcutScope } from "@/lib/editor/shortcutRegistry";
|
||||
import LightTooltip from "@/components/ui/LightTooltip.vue";
|
||||
import { formatShortcutDisplay } from "@/lib/editor/shortcutDisplay";
|
||||
import { COLUMN_NAME_COPY_SEPARATOR_LABELS, COLUMN_NAME_COPY_SEPARATOR_OPTIONS, isColumnNameCopySeparator, type ColumnNameCopySeparator } from "@/lib/dataGrid/dataGridColumnNameCopy";
|
||||
@@ -2065,6 +2065,9 @@ async function persistSettings() {
|
||||
// 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;
|
||||
// 同理:落盘口径可能改写键位(跨平台默认键、保留键回退),草稿要跟着回到
|
||||
// 落盘值,避免面板卡在“未保存”无法应用(#9881)。
|
||||
editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);
|
||||
}
|
||||
if (pendingBackgroundImageCleanup) {
|
||||
const cleanupPath = pendingBackgroundImageCleanup;
|
||||
@@ -2688,7 +2691,19 @@ function onShortcutKeydown(actionId: ShortcutActionId, event: KeyboardEvent) {
|
||||
editingShortcutId.value = null;
|
||||
return;
|
||||
}
|
||||
onShortcutChange(actionId, shortcut);
|
||||
// 草稿必须正好等于落盘值:否则“未保存”状态永远消不掉,#9881 里
|
||||
// 「应用」点了没反应、「应用并关闭」每次都弹未保存确认。
|
||||
const captured = resolveCapturedShortcutEdit(actionId, shortcut, editShortcuts.value);
|
||||
if (captured.rejectedByPlatformDefault) {
|
||||
toast(t("settings.shortcutPlatformDefault"), 3000);
|
||||
editingShortcutId.value = null;
|
||||
return;
|
||||
}
|
||||
if (captured.changed) {
|
||||
editShortcuts.value = captured.shortcuts;
|
||||
} else {
|
||||
onShortcutChange(actionId, shortcut);
|
||||
}
|
||||
editingShortcutId.value = null;
|
||||
}
|
||||
|
||||
@@ -2729,10 +2744,14 @@ function cancelShortcutEdit() {
|
||||
function resetShortcut(actionId: ShortcutActionId) {
|
||||
const definition = SHORTCUT_DEFINITIONS.find((item) => item.id === actionId);
|
||||
if (!definition) return;
|
||||
editShortcuts.value = {
|
||||
// The static definition default is the macOS literal for a few actions
|
||||
// (selection-occurrence), so the reset must go through the same persisted
|
||||
// normalization as capture — otherwise Windows drafts a key that can never
|
||||
// save and silently flips to the platform default on apply.
|
||||
editShortcuts.value = normalizeShortcutSettings({
|
||||
...editShortcuts.value,
|
||||
[actionId]: definition.defaultShortcut,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function clearShortcut(actionId: ShortcutActionId) {
|
||||
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const dialogSource = readFileSync(new URL("../EditorSettingsDialog.vue", import.meta.url), "utf8");
|
||||
|
||||
// Regression for https://github.com/t8y2/dbx/issues/9881: the shortcut draft was
|
||||
// written straight from the captured key event, while persistence re-normalizes it
|
||||
// and swaps "another platform's default" literals for the local platform default.
|
||||
// The draft then never equaled the stored value again, so `hasChanges()` stayed true
|
||||
// forever: "Apply" looked like it did nothing and "Apply and Close" always asked
|
||||
// about unsaved changes — the shortcut (and every other pending edit) could never be
|
||||
// saved. The capture path must therefore write the same value the store will keep.
|
||||
describe("EditorSettingsDialog shortcut capture normalization", () => {
|
||||
it("round-trips a captured combination through the persistence normalizer", () => {
|
||||
expect(dialogSource).toContain("resolveCapturedShortcutEdit,");
|
||||
expect(dialogSource).toContain("const captured = resolveCapturedShortcutEdit(actionId, shortcut, editShortcuts.value);");
|
||||
});
|
||||
|
||||
it("refuses combinations the store would rewrite, keeping the previous shortcut", () => {
|
||||
expect(dialogSource).toContain('toast(t("settings.shortcutPlatformDefault"), 3000);');
|
||||
|
||||
const guardStart = dialogSource.indexOf("if (captured.rejectedByPlatformDefault) {");
|
||||
const guardEnd = dialogSource.indexOf("if (captured.changed) {", guardStart);
|
||||
|
||||
expect(guardStart).toBeGreaterThan(-1);
|
||||
expect(guardEnd).toBeGreaterThan(guardStart);
|
||||
// 拒绝分支必须先结束编辑、且不得写入草稿(保留原值 → 草稿仍是落盘值的不动点)
|
||||
const guardBody = dialogSource.slice(guardStart, guardEnd);
|
||||
expect(guardBody).toContain("editingShortcutId.value = null;");
|
||||
expect(guardBody).toContain("return;");
|
||||
expect(guardBody).not.toContain("editShortcuts.value =");
|
||||
});
|
||||
|
||||
it("adopts the normalized result so the draft can never diverge from the stored value", () => {
|
||||
expect(dialogSource).toContain("editShortcuts.value = captured.shortcuts;");
|
||||
});
|
||||
|
||||
it("re-syncs the shortcut draft from the store after a successful apply", () => {
|
||||
const bodyStart = dialogSource.indexOf("async function persistSettings() {");
|
||||
const bodyEnd = dialogSource.indexOf("function applySettingsErrorToast(", bodyStart);
|
||||
|
||||
expect(bodyStart).toBeGreaterThan(-1);
|
||||
expect(bodyEnd).toBeGreaterThan(bodyStart);
|
||||
expect(dialogSource.slice(bodyStart, bodyEnd)).toContain("editShortcuts.value = normalizeShortcutSettings(settingsStore.editorSettings.shortcuts);");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user