feat(desktop): add global UI zoom settings and shortcuts
This commit is contained in:
parent
d35aeaa08c
commit
f7f5758c85
|
|
@ -43,8 +43,11 @@ import {
|
|||
isModRShortcut,
|
||||
isNewQueryShortcut,
|
||||
isObjectSourceSaveShortcutTarget,
|
||||
isResetZoomShortcut,
|
||||
isRefreshDataShortcut,
|
||||
isSaveShortcut,
|
||||
isZoomInShortcut,
|
||||
isZoomOutShortcut,
|
||||
} from "@/lib/keyboardShortcuts";
|
||||
import { isPreviewTab } from "@/lib/tabPresentation";
|
||||
import { supportsSqlFileExecution } from "@/lib/databaseCapabilities";
|
||||
|
|
@ -224,6 +227,47 @@ const saveSqlFolders = computed(() => {
|
|||
return tab ? savedSqlStore.listFolders(tab.connectionId) : [];
|
||||
});
|
||||
|
||||
async function applyUiScale(scale: number) {
|
||||
if (!isDesktop) return;
|
||||
try {
|
||||
const { getCurrentWebview } = await import("@tauri-apps/api/webview");
|
||||
await getCurrentWebview().setZoom(scale);
|
||||
} catch (error) {
|
||||
console.warn("[DBX] Failed to apply UI scale", { scale, error });
|
||||
}
|
||||
}
|
||||
|
||||
function setGlobalUiScale(scale: number) {
|
||||
settingsStore.updateEditorSettings({ uiScale: scale });
|
||||
}
|
||||
|
||||
function zoomInUi() {
|
||||
setGlobalUiScale(settingsStore.editorSettings.uiScale + 0.1);
|
||||
}
|
||||
|
||||
function zoomOutUi() {
|
||||
setGlobalUiScale(settingsStore.editorSettings.uiScale - 0.1);
|
||||
}
|
||||
|
||||
function resetUiZoom() {
|
||||
setGlobalUiScale(1);
|
||||
}
|
||||
|
||||
function isGlobalUiZoomTarget(target: EventTarget | null): target is Element {
|
||||
if (!(target instanceof Element)) return false;
|
||||
if (target.closest("[data-query-editor-root], [data-cell-detail-editor-root], [data-object-source-editor]")) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
target instanceof HTMLInputElement ||
|
||||
target instanceof HTMLTextAreaElement ||
|
||||
(target instanceof HTMLElement && target.isContentEditable)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return !target.closest("[contenteditable='true']");
|
||||
}
|
||||
|
||||
watch(
|
||||
() => queryStore.activeTabId,
|
||||
(id) => {
|
||||
|
|
@ -242,6 +286,14 @@ watch(
|
|||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
() => settingsStore.editorSettings.uiScale,
|
||||
(scale) => {
|
||||
void applyUiScale(scale);
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function toggleAiPanel() {
|
||||
showAiPanel.value = !showAiPanel.value;
|
||||
localStorage.setItem("dbx-ai-panel-open", String(showAiPanel.value));
|
||||
|
|
@ -667,6 +719,26 @@ function handleKeydown(e: KeyboardEvent) {
|
|||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (isDesktop && isGlobalUiZoomTarget(e.target)) {
|
||||
if (isZoomInShortcut(e, shortcuts)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
zoomInUi();
|
||||
return;
|
||||
}
|
||||
if (isZoomOutShortcut(e, shortcuts)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
zoomOutUi();
|
||||
return;
|
||||
}
|
||||
if (isResetZoomShortcut(e, shortcuts)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
resetUiZoom();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (isDesktop && isBrowserReloadShortcut(e)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
|
@ -733,6 +805,7 @@ onMounted(async () => {
|
|||
aiPanelReady.value = true;
|
||||
});
|
||||
applyTheme();
|
||||
void applyUiScale(settingsStore.editorSettings.uiScale);
|
||||
window.addEventListener("keydown", handleKeydown);
|
||||
window.addEventListener("dbx-open-driver-store", openDriverStoreFromEvent);
|
||||
if (isDesktop) {
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ const emit = defineEmits<{
|
|||
// Local edit state
|
||||
const editFontFamily = ref(settingsStore.editorSettings.fontFamily);
|
||||
const editFontSize = ref(settingsStore.editorSettings.fontSize);
|
||||
const editUiScale = ref(settingsStore.editorSettings.uiScale);
|
||||
const editTheme = ref(settingsStore.editorSettings.theme);
|
||||
const editExecuteMode = ref(settingsStore.editorSettings.executeMode);
|
||||
const editWordWrap = ref(settingsStore.editorSettings.wordWrap);
|
||||
|
|
@ -105,6 +106,7 @@ const redisScanPageSizeOptions = [200, 1000, 5000, 10000];
|
|||
const systemFonts = ref<string[]>([]);
|
||||
const systemFontsLoading = ref(false);
|
||||
const systemFontsLoaded = ref(false);
|
||||
const uiScaleOptions = [0.75, 0.9, 1, 1.1, 1.25, 1.5, 1.75, 2];
|
||||
|
||||
// --- Snippet state ---
|
||||
const editSnippets = ref<SqlSnippet[]>(settingsStore.editorSettings.snippets.map((s) => ({ ...s })));
|
||||
|
|
@ -219,6 +221,7 @@ watch(
|
|||
if (open) {
|
||||
editFontFamily.value = settingsStore.editorSettings.fontFamily;
|
||||
editFontSize.value = settingsStore.editorSettings.fontSize;
|
||||
editUiScale.value = settingsStore.editorSettings.uiScale;
|
||||
editTheme.value = settingsStore.editorSettings.theme;
|
||||
editExecuteMode.value = settingsStore.editorSettings.executeMode;
|
||||
editWordWrap.value = settingsStore.editorSettings.wordWrap;
|
||||
|
|
@ -256,6 +259,7 @@ function hasChanges(): boolean {
|
|||
return (
|
||||
editFontFamily.value !== settingsStore.editorSettings.fontFamily ||
|
||||
editFontSize.value !== settingsStore.editorSettings.fontSize ||
|
||||
editUiScale.value !== settingsStore.editorSettings.uiScale ||
|
||||
editTheme.value !== settingsStore.editorSettings.theme ||
|
||||
editExecuteMode.value !== settingsStore.editorSettings.executeMode ||
|
||||
editWordWrap.value !== settingsStore.editorSettings.wordWrap ||
|
||||
|
|
@ -280,6 +284,7 @@ async function applySettings() {
|
|||
settingsStore.updateEditorSettings({
|
||||
fontFamily: editFontFamily.value,
|
||||
fontSize: editFontSize.value,
|
||||
uiScale: editUiScale.value,
|
||||
theme: editTheme.value,
|
||||
executeMode: editExecuteMode.value,
|
||||
wordWrap: editWordWrap.value,
|
||||
|
|
@ -304,6 +309,7 @@ async function applySettings() {
|
|||
function resetDefaults() {
|
||||
editFontFamily.value = DEFAULT_EDITOR_SETTINGS.fontFamily;
|
||||
editFontSize.value = DEFAULT_EDITOR_SETTINGS.fontSize;
|
||||
editUiScale.value = DEFAULT_EDITOR_SETTINGS.uiScale;
|
||||
editTheme.value = DEFAULT_EDITOR_SETTINGS.theme;
|
||||
editExecuteMode.value = DEFAULT_EDITOR_SETTINGS.executeMode;
|
||||
editWordWrap.value = DEFAULT_EDITOR_SETTINGS.wordWrap;
|
||||
|
|
@ -1106,6 +1112,31 @@ watch(
|
|||
</section>
|
||||
|
||||
<section v-else-if="activeSettingsTab === 'appearance'" class="flex flex-col gap-5 py-2">
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.uiScale") }}</Label>
|
||||
<Select
|
||||
:model-value="String(editUiScale)"
|
||||
@update:model-value="
|
||||
(value: any) => {
|
||||
const next = Number(value);
|
||||
if (Number.isFinite(next)) editUiScale = next;
|
||||
}
|
||||
"
|
||||
>
|
||||
<SelectTrigger class="w-40">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="scale in uiScaleOptions" :key="scale" :value="String(scale)">
|
||||
{{ Math.round(scale * 100) }}%
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<p class="text-xs text-muted-foreground">{{ t("settings.uiScaleDescription") }}</p>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label>{{ t("settings.appLayout") }}</Label>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
|
|
|
|||
|
|
@ -383,34 +383,6 @@ function runKeymapExtension(codeMirrorKeymap: (typeof import("@codemirror/view")
|
|||
]),
|
||||
) ?? [],
|
||||
codeMirrorKeymap.of([
|
||||
{
|
||||
key: "Mod-=",
|
||||
run: () => {
|
||||
zoomIn();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "Mod-+",
|
||||
run: () => {
|
||||
zoomIn();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "Mod--",
|
||||
run: () => {
|
||||
zoomOut();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "Mod-0",
|
||||
run: () => {
|
||||
resetZoom();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
{
|
||||
key: shortcutToCodeMirrorKey(shortcuts.acceptCompletion),
|
||||
run: (view) => codeMirrorAcceptCompletion?.(view) ?? false,
|
||||
|
|
|
|||
|
|
@ -1386,6 +1386,9 @@ export default {
|
|||
noFontsFound: "No fonts found",
|
||||
useCustomFont: "Use “{font}”",
|
||||
fontSize: "Font Size",
|
||||
uiScale: "UI Scale",
|
||||
uiScaleDescription:
|
||||
"Scale the entire desktop UI for high-DPI displays. Changes apply immediately and are restored on next launch.",
|
||||
theme: "Theme",
|
||||
selectTheme: "Select theme...",
|
||||
followAppTheme: "Follow app theme",
|
||||
|
|
@ -1480,6 +1483,9 @@ export default {
|
|||
shortcutNewQuery: "New query",
|
||||
shortcutCloseTab: "Close tab",
|
||||
shortcutFocusSearch: "Focus search",
|
||||
shortcutZoomInUi: "Zoom in UI",
|
||||
shortcutZoomOutUi: "Zoom out UI",
|
||||
shortcutResetUiZoom: "Reset UI zoom",
|
||||
shortcutRefreshData: "Refresh data",
|
||||
shortcutToggleTranspose: "Toggle transpose view",
|
||||
shortcutCancelSearch: "Cancel search",
|
||||
|
|
|
|||
|
|
@ -1280,6 +1280,9 @@ export default {
|
|||
noFontsFound: "No se encontraron fuentes",
|
||||
useCustomFont: "Usar “{font}”",
|
||||
fontSize: "Tamaño de fuente",
|
||||
uiScale: "Escala de interfaz",
|
||||
uiScaleDescription:
|
||||
"Escala toda la interfaz de escritorio para pantallas de alta densidad. Los cambios se aplican al instante y se restauran al volver a abrir.",
|
||||
theme: "Tema",
|
||||
selectTheme: "Seleccionar tema...",
|
||||
appLayout: "Diseño de la interfaz",
|
||||
|
|
@ -1373,6 +1376,9 @@ export default {
|
|||
shortcutNewQuery: "Nueva consulta",
|
||||
shortcutCloseTab: "Cerrar pestaña",
|
||||
shortcutFocusSearch: "Enfocar búsqueda",
|
||||
shortcutZoomInUi: "Ampliar interfaz",
|
||||
shortcutZoomOutUi: "Reducir interfaz",
|
||||
shortcutResetUiZoom: "Restablecer zoom de interfaz",
|
||||
shortcutRefreshData: "Actualizar datos",
|
||||
shortcutToggleTranspose: "Alternar vista transpuesta",
|
||||
shortcutCancelSearch: "Cancelar búsqueda",
|
||||
|
|
|
|||
|
|
@ -1361,6 +1361,8 @@ export default {
|
|||
noFontsFound: "未找到字体",
|
||||
useCustomFont: "使用“{font}”",
|
||||
fontSize: "字号",
|
||||
uiScale: "界面缩放",
|
||||
uiScaleDescription: "按比例缩放整个桌面端界面,适合高清屏;修改后立即生效,并在下次启动时恢复。",
|
||||
theme: "主题",
|
||||
selectTheme: "选择主题...",
|
||||
followAppTheme: "跟随应用主题",
|
||||
|
|
@ -1447,6 +1449,9 @@ export default {
|
|||
shortcutNewQuery: "新建查询",
|
||||
shortcutCloseTab: "关闭标签页",
|
||||
shortcutFocusSearch: "聚焦搜索",
|
||||
shortcutZoomInUi: "放大全局界面",
|
||||
shortcutZoomOutUi: "缩小全局界面",
|
||||
shortcutResetUiZoom: "重置全局界面缩放",
|
||||
shortcutRefreshData: "刷新数据",
|
||||
shortcutToggleTranspose: "切换转置视图",
|
||||
shortcutCancelSearch: "取消搜索",
|
||||
|
|
|
|||
|
|
@ -92,6 +92,27 @@ export function isModRShortcut(event: ShortcutLikeEvent): boolean {
|
|||
return matchesShortcut(event, "Mod+R");
|
||||
}
|
||||
|
||||
export function isZoomInShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
|
||||
if (matchesShortcut(event, actionShortcut("zoomInUi", shortcuts))) return true;
|
||||
if (event.isComposing || event.altKey) return false;
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
return normalizeKey(event.key) === "NumpadAdd" && !event.shiftKey;
|
||||
}
|
||||
|
||||
export function isZoomOutShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
|
||||
if (matchesShortcut(event, actionShortcut("zoomOutUi", shortcuts))) return true;
|
||||
if (event.isComposing || event.altKey) return false;
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
return normalizeKey(event.key) === "NumpadSubtract" && !event.shiftKey;
|
||||
}
|
||||
|
||||
export function isResetZoomShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
|
||||
if (matchesShortcut(event, actionShortcut("resetUiZoom", shortcuts))) return true;
|
||||
if (event.isComposing || event.altKey || event.shiftKey) return false;
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
return normalizeKey(event.key) === "Numpad0";
|
||||
}
|
||||
|
||||
export function isToggleTransposeShortcut(event: ShortcutLikeEvent, shortcuts?: Partial<ShortcutSettings>): boolean {
|
||||
return matchesShortcut(event, actionShortcut("toggleTranspose", shortcuts));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ export type ShortcutActionId =
|
|||
| "newQuery"
|
||||
| "closeTab"
|
||||
| "focusSearch"
|
||||
| "zoomInUi"
|
||||
| "zoomOutUi"
|
||||
| "resetUiZoom"
|
||||
| "find"
|
||||
| "replace"
|
||||
| "refreshData"
|
||||
|
|
@ -73,6 +76,24 @@ export const SHORTCUT_DEFINITIONS: ShortcutDefinition[] = [
|
|||
scope: "global",
|
||||
defaultShortcut: "Mod+F",
|
||||
},
|
||||
{
|
||||
id: "zoomInUi",
|
||||
labelKey: "settings.shortcutZoomInUi",
|
||||
scope: "global",
|
||||
defaultShortcut: "Mod+=",
|
||||
},
|
||||
{
|
||||
id: "zoomOutUi",
|
||||
labelKey: "settings.shortcutZoomOutUi",
|
||||
scope: "global",
|
||||
defaultShortcut: "Mod+-",
|
||||
},
|
||||
{
|
||||
id: "resetUiZoom",
|
||||
labelKey: "settings.shortcutResetUiZoom",
|
||||
scope: "global",
|
||||
defaultShortcut: "Mod+0",
|
||||
},
|
||||
{
|
||||
id: "find",
|
||||
labelKey: "settings.shortcutFind",
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ export type EditorTheme =
|
|||
export interface EditorSettings {
|
||||
fontFamily: string;
|
||||
fontSize: number;
|
||||
uiScale: number;
|
||||
theme: EditorTheme;
|
||||
executeMode: "all" | "current";
|
||||
wordWrap: boolean;
|
||||
|
|
@ -222,6 +223,7 @@ export const FONT_FAMILIES: { value: string; label: string }[] = [
|
|||
export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||
fontSize: 13,
|
||||
uiScale: 1,
|
||||
theme: "app",
|
||||
executeMode: "all",
|
||||
wordWrap: false,
|
||||
|
|
@ -245,6 +247,13 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
|||
|
||||
export const STORAGE_KEY = "dbx-editor-settings";
|
||||
const OLD_FONT_SIZE_KEY = "dbx-query-editor-font-size";
|
||||
const MIN_UI_SCALE = 0.75;
|
||||
const MAX_UI_SCALE = 2;
|
||||
|
||||
function normalizeUiScale(value: unknown): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value)) return DEFAULT_EDITOR_SETTINGS.uiScale;
|
||||
return Math.min(MAX_UI_SCALE, Math.max(MIN_UI_SCALE, Math.round(value * 100) / 100));
|
||||
}
|
||||
|
||||
function normalizeColumnFormatters(value: unknown): Record<string, ColumnFormatterConfig> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
||||
|
|
@ -301,6 +310,7 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
|
|||
return {
|
||||
fontFamily: settings.fontFamily ?? DEFAULT_EDITOR_SETTINGS.fontFamily,
|
||||
fontSize: settings.fontSize ?? DEFAULT_EDITOR_SETTINGS.fontSize,
|
||||
uiScale: normalizeUiScale(settings.uiScale),
|
||||
theme: settings.theme && EDITOR_THEME_VALUES.has(settings.theme) ? settings.theme : DEFAULT_EDITOR_SETTINGS.theme,
|
||||
executeMode: settings.executeMode ?? DEFAULT_EDITOR_SETTINGS.executeMode,
|
||||
wordWrap: settings.wordWrap ?? DEFAULT_EDITOR_SETTINGS.wordWrap,
|
||||
|
|
@ -427,6 +437,7 @@ export const useSettingsStore = defineStore("settings", () => {
|
|||
function updateEditorSettings(partial: Partial<EditorSettings>) {
|
||||
if (partial.fontFamily !== undefined) editorSettings.value.fontFamily = partial.fontFamily;
|
||||
if (partial.fontSize !== undefined) editorSettings.value.fontSize = partial.fontSize;
|
||||
if (partial.uiScale !== undefined) editorSettings.value.uiScale = normalizeUiScale(partial.uiScale);
|
||||
if (partial.theme !== undefined) editorSettings.value.theme = partial.theme;
|
||||
if (partial.executeMode !== undefined) editorSettings.value.executeMode = partial.executeMode;
|
||||
if (partial.wordWrap !== undefined) editorSettings.value.wordWrap = partial.wordWrap;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
import { strict as assert } from "node:assert";
|
||||
import { readFileSync } from "node:fs";
|
||||
import test from "node:test";
|
||||
|
||||
test("desktop capability allows setting the webview zoom", () => {
|
||||
const capability = JSON.parse(readFileSync("src-tauri/capabilities/default.json", "utf8")) as {
|
||||
permissions: string[];
|
||||
};
|
||||
|
||||
assert.equal(capability.permissions.includes("core:webview:allow-set-webview-zoom"), true);
|
||||
});
|
||||
|
||||
test("app logs webview zoom failures instead of swallowing them silently", () => {
|
||||
const source = readFileSync("apps/desktop/src/App.vue", "utf8");
|
||||
|
||||
assert.match(source, /getCurrentWebview\(\)\.setZoom\(scale\)/);
|
||||
assert.match(source, /console\.warn\("\[DBX\] Failed to apply UI scale"/);
|
||||
assert.match(source, /applyUiScale\(settingsStore\.editorSettings\.uiScale\)/);
|
||||
});
|
||||
|
|
@ -10,11 +10,14 @@ import {
|
|||
isModRShortcut,
|
||||
isNewQueryShortcut,
|
||||
isObjectSourceSaveShortcutTarget,
|
||||
isResetZoomShortcut,
|
||||
isRefreshDataShortcut,
|
||||
isSaveShortcut,
|
||||
isCopyCurrentRowShortcut,
|
||||
isDeleteCurrentRowShortcut,
|
||||
isToggleTransposeShortcut,
|
||||
isZoomInShortcut,
|
||||
isZoomOutShortcut,
|
||||
} from "../../apps/desktop/src/lib/keyboardShortcuts.ts";
|
||||
import { shortcutToCodeMirrorKey } from "../../apps/desktop/src/lib/shortcutRegistry.ts";
|
||||
|
||||
|
|
@ -119,6 +122,27 @@ test("matches Mod-R without shift or alt for scoped refresh and replace", () =>
|
|||
assert.equal(isModRShortcut({ key: "r", ctrlKey: true, altKey: true }), false);
|
||||
});
|
||||
|
||||
test("matches desktop UI zoom shortcuts", () => {
|
||||
assert.equal(isZoomInShortcut({ key: "=", ctrlKey: true }), true);
|
||||
assert.equal(isZoomInShortcut({ key: "NumpadAdd", ctrlKey: true }), true);
|
||||
assert.equal(isZoomOutShortcut({ key: "-", ctrlKey: true }), true);
|
||||
assert.equal(isZoomOutShortcut({ key: "NumpadSubtract", metaKey: true }), true);
|
||||
assert.equal(isResetZoomShortcut({ key: "0", ctrlKey: true }), true);
|
||||
assert.equal(isResetZoomShortcut({ key: "Numpad0", metaKey: true }), true);
|
||||
});
|
||||
|
||||
test("matches configurable desktop UI zoom shortcuts", () => {
|
||||
assert.equal(isZoomInShortcut({ key: "i", ctrlKey: true }, { zoomInUi: "Mod+I" } as any), true);
|
||||
assert.equal(isZoomOutShortcut({ key: "o", metaKey: true }, { zoomOutUi: "Mod+O" } as any), true);
|
||||
assert.equal(isResetZoomShortcut({ key: "9", ctrlKey: true }, { resetUiZoom: "Mod+9" } as any), true);
|
||||
});
|
||||
|
||||
test("ignores desktop UI zoom shortcuts with the wrong modifiers", () => {
|
||||
assert.equal(isZoomInShortcut({ key: "=", ctrlKey: true, altKey: true }), false);
|
||||
assert.equal(isZoomOutShortcut({ key: "-", isComposing: true, ctrlKey: true }), false);
|
||||
assert.equal(isResetZoomShortcut({ key: "0", metaKey: true, shiftKey: true }), false);
|
||||
});
|
||||
|
||||
test("ignores focus search shortcut while composing", () => {
|
||||
assert.equal(isFocusSearchShortcut({ key: "f", ctrlKey: true, isComposing: true }), false);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ test("query editor localizes the replace all button", () => {
|
|||
assert.doesNotMatch(searchPanelSource, />\s*全部\s*</);
|
||||
});
|
||||
|
||||
test("query editor no longer binds keyboard shortcuts for editor font zoom", () => {
|
||||
assert.doesNotMatch(source, /key:\s*"Mod-="/);
|
||||
assert.doesNotMatch(source, /key:\s*"Mod-\+"/);
|
||||
assert.doesNotMatch(source, /key:\s*"Mod--"/);
|
||||
assert.doesNotMatch(source, /key:\s*"Mod-0"/);
|
||||
});
|
||||
|
||||
test("query editor exposes a context menu for executing selected SQL", () => {
|
||||
assert.match(source, /ContextMenuContent/);
|
||||
assert.match(source, /data-context-menu/);
|
||||
|
|
@ -67,3 +74,13 @@ test("app keydown routes Mod-R directly before browser reload handling", () => {
|
|||
assert.match(contentAreaSource, /dataGridRef\.value\?\.openCellDetailSearch\(\)/);
|
||||
assert.match(contentAreaSource, /if \(target\.closest\("\[data-grid-root\]"\)\) return refreshData\(\)/);
|
||||
});
|
||||
|
||||
test("app routes global UI zoom shortcuts across editor surfaces", () => {
|
||||
assert.match(appSource, /const shortcuts = settingsStore\.editorSettings\.shortcuts;/);
|
||||
assert.match(appSource, /isZoomInShortcut\(e, shortcuts\)/);
|
||||
assert.match(appSource, /isZoomOutShortcut\(e, shortcuts\)/);
|
||||
assert.match(appSource, /isResetZoomShortcut\(e, shortcuts\)/);
|
||||
assert.match(appSource, /isGlobalUiZoomTarget\(e\.target\)/);
|
||||
assert.match(appSource, /settingsStore\.updateEditorSettings\(\{\s*uiScale:\s*scale\s*\}\)/);
|
||||
assert.match(appSource, /\[data-query-editor-root\], \[data-cell-detail-editor-root\], \[data-object-source-editor\]/);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ test("defaults shortcut settings", () => {
|
|||
assert.equal(settings.shortcuts.deleteCurrentRow, "Delete");
|
||||
assert.equal(settings.shortcuts.newQuery, "Mod+T");
|
||||
assert.equal(settings.shortcuts.focusSearch, "Mod+F");
|
||||
assert.equal(settings.shortcuts.zoomInUi, "Mod+=");
|
||||
assert.equal(settings.shortcuts.zoomOutUi, "Mod+-");
|
||||
assert.equal(settings.shortcuts.resetUiZoom, "Mod+0");
|
||||
assert.equal(settings.shortcuts.refreshData, "F5");
|
||||
assert.equal(settings.shortcuts.toggleTranspose, "Tab");
|
||||
});
|
||||
|
|
@ -51,6 +54,7 @@ test("keeps saved shortcut overrides", () => {
|
|||
copyCurrentRow: "Alt+Shift+D",
|
||||
deleteCurrentRow: "Backspace",
|
||||
newQuery: "Shift+Mod+N",
|
||||
zoomInUi: "Alt+Mod+=",
|
||||
} as any,
|
||||
});
|
||||
|
||||
|
|
@ -58,6 +62,7 @@ test("keeps saved shortcut overrides", () => {
|
|||
assert.equal(settings.shortcuts.copyCurrentRow, "Alt+Shift+D");
|
||||
assert.equal(settings.shortcuts.deleteCurrentRow, "Backspace");
|
||||
assert.equal(settings.shortcuts.newQuery, "Shift+Mod+N");
|
||||
assert.equal(settings.shortcuts.zoomInUi, "Alt+Mod+=");
|
||||
assert.equal(settings.shortcuts.saveSql, "Mod+S");
|
||||
});
|
||||
|
||||
|
|
@ -180,3 +185,18 @@ test("infers legacy AI provider from saved endpoint and model", () => {
|
|||
assert.equal(deepseek.endpoint, "https://api.deepseek.com/anthropic/v1/messages");
|
||||
assert.equal(deepseek.model, "deepseek-v4-pro");
|
||||
});
|
||||
|
||||
test("normalizeEditorSettings falls back to the default UI scale", () => {
|
||||
const settings = normalizeEditorSettings({});
|
||||
|
||||
assert.equal(settings.uiScale, DEFAULT_EDITOR_SETTINGS.uiScale);
|
||||
});
|
||||
|
||||
test("normalizeEditorSettings clamps UI scale into the supported range", () => {
|
||||
assert.equal(normalizeEditorSettings({ uiScale: 0.2 }).uiScale, 0.75);
|
||||
assert.equal(normalizeEditorSettings({ uiScale: 2.8 }).uiScale, 2);
|
||||
});
|
||||
|
||||
test("normalizeEditorSettings keeps valid UI scales with two-decimal precision", () => {
|
||||
assert.equal(normalizeEditorSettings({ uiScale: 1.125 }).uiScale, 1.13);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
"core:window:allow-is-maximized",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-start-dragging",
|
||||
"core:webview:allow-set-webview-zoom",
|
||||
"dialog:default",
|
||||
"dialog:allow-save",
|
||||
"dialog:allow-open",
|
||||
|
|
|
|||
Loading…
Reference in New Issue