feat(settings): preview theme and UI fonts on hover
This commit is contained in:
8 files changed
+194
-17
No files matched your search
@@ -218,6 +218,7 @@ import { apiUrl, webPath } from "@/lib/common/webPath";
|
||||
import { DEFAULT_DATA_GRID_FONT_FAMILY, DEFAULT_UI_FONT_FAMILY, normalizeCustomFontFamilyInput, readableFontFamily, SYSTEM_UI_FONT_FAMILY } from "@/lib/app/appFonts";
|
||||
import { buildFontFamilyOptions, displayFontFamily, isPresetFontFamily, loadSystemFontNames } from "@/lib/app/fontFamilyOptions";
|
||||
import { buildAppSupportInfoRows, formatAppSupportInfoForClipboard, type AppSupportInfoLabels } from "@/lib/app/supportInfo";
|
||||
import { useUiFontFamilyPreview } from "@/composables/useUiFontFamilyPreview";
|
||||
import { DateTimePatterns, normalizeSupportedDateTimePattern } from "@/lib/dataGrid/columnFormatter";
|
||||
import { MAX_RESULT_PAGE_SIZE, MIN_RESULT_PAGE_SIZE } from "@/lib/dataGrid/paginationPageSize";
|
||||
import { MAX_QUERY_RESULT_MAX_ROWS } from "@/lib/dataGrid/queryResultRowLimit";
|
||||
@@ -234,12 +235,37 @@ const connectionStore = useConnectionStore();
|
||||
const savedSqlStore = useSavedSqlStore();
|
||||
const promptTemplateStore = usePromptTemplateStore();
|
||||
const tunnelProfileStore = useTunnelProfileStore();
|
||||
const { isDark, themeMode, themePalette, activeCustomUiColors, cornerStyle, setThemeMode, setThemePalette, setCustomUiColors, resetCustomUiColors, setCornerStyle } = useTheme();
|
||||
const { isDark, themeMode, themePalette, activeCustomUiColors, cornerStyle, setThemeMode, setThemePalette, previewThemePalette, clearThemePalettePreview, setCustomUiColors, resetCustomUiColors, setCornerStyle } = useTheme();
|
||||
const { previewUiFontFamily, clearUiFontFamilyPreview } = useUiFontFamilyPreview();
|
||||
|
||||
function updateCustomUiColor(key: keyof AppCustomUiColors, value: string) {
|
||||
setCustomUiColors({ ...activeCustomUiColors.value, [key]: value });
|
||||
}
|
||||
|
||||
function onThemePaletteSelect(value: unknown) {
|
||||
if (typeof value === "string") setThemePalette(value as AppThemePalette);
|
||||
}
|
||||
|
||||
function onThemePaletteOpenChange(open: boolean) {
|
||||
if (!open) clearThemePalettePreview();
|
||||
}
|
||||
|
||||
function previewUiFontOption(value: string | undefined) {
|
||||
if (value) previewUiFontFamily(value);
|
||||
}
|
||||
|
||||
function restoreUiFontFamilyPreview() {
|
||||
previewUiFontFamily(editUiFontFamily.value);
|
||||
}
|
||||
|
||||
function onUiFontFamilyOpenChange(open: boolean) {
|
||||
if (open) {
|
||||
void loadSystemFontOptions();
|
||||
} else {
|
||||
restoreUiFontFamilyPreview();
|
||||
}
|
||||
}
|
||||
|
||||
const appThemePaletteOptions = computed(
|
||||
(): Array<{ value: AppThemePalette; label: string; previewColor: string }> =>
|
||||
APP_THEME_PALETTES.map((palette) => ({
|
||||
@@ -1496,6 +1522,20 @@ watch(
|
||||
editMetadataCacheMaxMemoryMb.value = settingsStore.desktopSettings.metadata_cache_max_memory_mb;
|
||||
editDuckDbWorkerProcessIsolation.value = settingsStore.desktopSettings.duckdb_worker_process_isolation;
|
||||
editSidebarTablePageSize.value = settingsStore.desktopSettings.sidebar_table_page_size ?? DEFAULT_SIDEBAR_TABLE_PAGE_SIZE;
|
||||
} else {
|
||||
clearThemePalettePreview();
|
||||
clearUiFontFamilyPreview();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => settingsStore.settingsPageActive,
|
||||
(active) => {
|
||||
if (isSettingsPage.value && !active) {
|
||||
clearThemePalettePreview();
|
||||
clearUiFontFamilyPreview();
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
@@ -2044,7 +2084,10 @@ function onTableFontFamilyChange(v: any) {
|
||||
}
|
||||
|
||||
function onUiFontFamilyChange(v: any) {
|
||||
if (typeof v === "string") editUiFontFamily.value = v;
|
||||
if (typeof v === "string") {
|
||||
editUiFontFamily.value = v;
|
||||
previewUiFontFamily(v);
|
||||
}
|
||||
}
|
||||
|
||||
const themeSelectValue = computed(() => {
|
||||
@@ -3717,6 +3760,8 @@ onMounted(() => {
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
clearThemePalettePreview();
|
||||
clearUiFontFamilyPreview();
|
||||
cleanupTableColumnTemplatePointerDrag();
|
||||
cleanupTruncationObservers();
|
||||
});
|
||||
@@ -5682,7 +5727,7 @@ onUnmounted(() => {
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="min-w-0 flex-1">
|
||||
<Select :model-value="themePalette" @update:model-value="(value) => setThemePalette(value as AppThemePalette)">
|
||||
<Select :model-value="themePalette" @update:model-value="onThemePaletteSelect" @update:open="onThemePaletteOpenChange">
|
||||
<SelectTrigger class="h-8 w-full gap-1">
|
||||
<SelectValue :placeholder="t('settings.selectColorTheme')">
|
||||
<span v-if="selectedThemePaletteOption" class="flex min-w-0 items-center gap-1">
|
||||
@@ -5696,8 +5741,8 @@ onUnmounted(() => {
|
||||
</span>
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="option in appThemePaletteOptions" :key="option.value" :value="option.value">
|
||||
<SelectContent @pointerleave="clearThemePalettePreview">
|
||||
<SelectItem v-for="option in appThemePaletteOptions" :key="option.value" :value="option.value" @pointerenter="previewThemePalette(option.value)" @focus="previewThemePalette(option.value)">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="h-3 w-3 rounded-full border border-border shadow-xs" :style="{ background: option.previewColor }" />
|
||||
{{ option.label }}
|
||||
@@ -5776,7 +5821,10 @@ onUnmounted(() => {
|
||||
:trigger-icon-class="appearanceFontSearchTriggerIconClass"
|
||||
content-class="w-[var(--reka-popover-trigger-width)] min-w-[260px]"
|
||||
@update:model-value="onUiFontFamilyChange"
|
||||
@update:open="(open: boolean) => open && loadSystemFontOptions()"
|
||||
@update:open="onUiFontFamilyOpenChange"
|
||||
@option-hover="previewUiFontOption"
|
||||
@option-highlight="previewUiFontOption"
|
||||
@option-leave="restoreUiFontFamilyPreview"
|
||||
>
|
||||
<template #trigger-label="{ label, loading }">
|
||||
<span class="truncate" :style="{ fontFamily: editUiFontFamily }">
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const dialogSource = readFileSync(new URL("../EditorSettingsDialog.vue", import.meta.url), "utf8");
|
||||
const appSource = readFileSync(new URL("../../../App.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("EditorSettingsDialog preview cleanup", () => {
|
||||
it("keeps the existing draft lifecycle independent from page activity", () => {
|
||||
expect(dialogSource).toContain("const settingsVisible = computed(() => isSettingsPage.value || props.open === true);");
|
||||
expect(dialogSource).toContain("syncEditorSettingsDraftFromStore();");
|
||||
});
|
||||
|
||||
it("clears previews when a mounted settings page becomes inactive", () => {
|
||||
expect(appSource).toContain('v-if="settingsPageTabOpen"');
|
||||
expect(appSource).toContain('v-show="settingsStore.settingsPageActive"');
|
||||
expect(dialogSource).toContain("() => settingsStore.settingsPageActive");
|
||||
expect(dialogSource).toContain("if (isSettingsPage.value && !active)");
|
||||
expect(dialogSource).toContain("clearThemePalettePreview();");
|
||||
expect(dialogSource).toContain("clearUiFontFamilyPreview();");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user