add: 将loadTheme抽象到外部模块,使用DEFAULT_EDITOR_SETTINGS默认值,增加老的配置项迁移
This commit is contained in:
parent
e5f3f3bad8
commit
4d2c34ecb1
|
|
@ -1,7 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch, shallowRef, computed } from "vue";
|
||||
import type { EditorView as EditorViewType } from "@codemirror/view";
|
||||
import type { Extension } from "@codemirror/state";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Settings } from "lucide-vue-next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -13,7 +12,10 @@ import {
|
|||
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { useSettingsStore, EDITOR_THEMES, FONT_FAMILIES, type EditorTheme } from "@/stores/settingsStore";
|
||||
import {
|
||||
useSettingsStore, EDITOR_THEMES, FONT_FAMILIES, DEFAULT_EDITOR_SETTINGS,
|
||||
} from "@/stores/settingsStore";
|
||||
import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
|
||||
|
||||
const { t } = useI18n();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
|
@ -58,9 +60,9 @@ function applySettings() {
|
|||
}
|
||||
|
||||
function resetDefaults() {
|
||||
editFontFamily.value = "'JetBrains Mono', 'Fira Code', monospace";
|
||||
editFontSize.value = 13;
|
||||
editTheme.value = "one-dark";
|
||||
editFontFamily.value = DEFAULT_EDITOR_SETTINGS.fontFamily;
|
||||
editFontSize.value = DEFAULT_EDITOR_SETTINGS.fontSize;
|
||||
editTheme.value = DEFAULT_EDITOR_SETTINGS.theme;
|
||||
}
|
||||
|
||||
function onFontFamilyChange(v: any) {
|
||||
|
|
@ -68,78 +70,36 @@ function onFontFamilyChange(v: any) {
|
|||
}
|
||||
|
||||
function onThemeChange(v: any) {
|
||||
if (typeof v === 'string') editTheme.value = v as EditorTheme;
|
||||
if (typeof v === 'string') editTheme.value = v as typeof DEFAULT_EDITOR_SETTINGS.theme;
|
||||
}
|
||||
|
||||
// ---------- CodeMirror preview ----------
|
||||
const previewRef = ref<HTMLDivElement>();
|
||||
const previewView = shallowRef<EditorViewType | null>(null);
|
||||
|
||||
// Reactive settings computed for the preview
|
||||
const previewSettings = computed(() => ({
|
||||
fontFamily: editFontFamily.value,
|
||||
fontSize: editFontSize.value,
|
||||
theme: editTheme.value,
|
||||
}));
|
||||
|
||||
async function loadTheme(theme: EditorTheme): Promise<Extension> {
|
||||
switch (theme) {
|
||||
case "one-dark":
|
||||
return (await import("@codemirror/theme-one-dark")).oneDark;
|
||||
case "vscode-dark":
|
||||
return (await import("@uiw/codemirror-theme-vscode")).vscodeDark;
|
||||
case "vscode-light":
|
||||
return (await import("@uiw/codemirror-theme-vscode")).vscodeLight;
|
||||
case "nord":
|
||||
return (await import("@uiw/codemirror-theme-nord")).nord;
|
||||
case "okaidia":
|
||||
return (await import("@uiw/codemirror-theme-okaidia")).okaidia;
|
||||
case "material":
|
||||
return (await import("@uiw/codemirror-theme-material")).materialDark;
|
||||
case "duotone-light":
|
||||
return (await import("@uiw/codemirror-theme-duotone")).duotoneLight;
|
||||
case "duotone-dark":
|
||||
return (await import("@uiw/codemirror-theme-duotone")).duotoneDark;
|
||||
case "xcode":
|
||||
return (await import("@uiw/codemirror-theme-xcode")).xcodeLight;
|
||||
default:
|
||||
return (await import("@codemirror/theme-one-dark")).oneDark;
|
||||
}
|
||||
}
|
||||
|
||||
const previewSql = `SELECT u.id, u.name
|
||||
FROM users u
|
||||
ORDER BY u.id LIMIT 5;`;
|
||||
|
||||
let fontSizeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let fontFamilyComp: import("@codemirror/state").Compartment | null = null;
|
||||
let fontThemeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let themeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let editorViewModule: typeof import("@codemirror/view") | null = null;
|
||||
|
||||
watch(previewSettings, async (ss) => {
|
||||
if (
|
||||
!previewView.value ||
|
||||
!fontSizeComp ||
|
||||
!fontFamilyComp ||
|
||||
!themeComp ||
|
||||
!editorViewModule
|
||||
) return;
|
||||
if (!previewView.value || !fontThemeComp || !themeComp || !editorViewModule) return;
|
||||
|
||||
const themeExt = await loadTheme(ss.theme);
|
||||
const themeExt = await loadEditorTheme(ss.theme);
|
||||
previewView.value.dispatch({
|
||||
effects: [
|
||||
themeComp.reconfigure(themeExt),
|
||||
fontFamilyComp.reconfigure(
|
||||
editorViewModule.EditorView.theme({
|
||||
"&": { fontSize: `${ss.fontSize}px` },
|
||||
".cm-content": { fontFamily: ss.fontFamily },
|
||||
})
|
||||
),
|
||||
fontSizeComp.reconfigure(
|
||||
editorViewModule.EditorView.theme({
|
||||
"&": { fontSize: `${ss.fontSize}px` },
|
||||
".cm-content": { fontFamily: ss.fontFamily },
|
||||
})
|
||||
fontThemeComp.reconfigure(
|
||||
editorFontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily)
|
||||
),
|
||||
],
|
||||
});
|
||||
|
|
@ -165,12 +125,11 @@ watch(previewRef, async (el) => {
|
|||
]);
|
||||
|
||||
editorViewModule = { EditorView } as typeof import("@codemirror/view");
|
||||
fontSizeComp = new Compartment();
|
||||
fontFamilyComp = new Compartment();
|
||||
fontThemeComp = new Compartment();
|
||||
themeComp = new Compartment();
|
||||
|
||||
const ss = previewSettings.value;
|
||||
const themeExt = await loadTheme(ss.theme);
|
||||
const themeExt = await loadEditorTheme(ss.theme);
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: previewSql,
|
||||
|
|
@ -178,22 +137,10 @@ watch(previewRef, async (el) => {
|
|||
basicSetup,
|
||||
sql({ dialect: MySQL }),
|
||||
themeComp.of(themeExt),
|
||||
fontSizeComp.of(
|
||||
EditorView.theme({
|
||||
"&": { fontSize: `${ss.fontSize}px` },
|
||||
".cm-scroller": { overflow: "auto" },
|
||||
".cm-content": { fontFamily: ss.fontFamily },
|
||||
})
|
||||
),
|
||||
fontFamilyComp.of(
|
||||
EditorView.theme({
|
||||
"&": { fontSize: `${ss.fontSize}px` },
|
||||
".cm-scroller": { overflow: "auto" },
|
||||
".cm-content": { fontFamily: ss.fontFamily },
|
||||
})
|
||||
fontThemeComp.of(
|
||||
editorFontTheme(EditorView, ss.fontSize, ss.fontFamily)
|
||||
),
|
||||
],
|
||||
// readOnly: true,
|
||||
});
|
||||
|
||||
previewView.value = new EditorView({ state, parent: previewRef.value });
|
||||
|
|
@ -204,8 +151,7 @@ watch(() => props.open, (open) => {
|
|||
previewView.value.destroy();
|
||||
previewView.value = null;
|
||||
previewInitialized = false;
|
||||
fontSizeComp = null;
|
||||
fontFamilyComp = null;
|
||||
fontThemeComp = null;
|
||||
themeComp = null;
|
||||
editorViewModule = null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
import { ref, onMounted, onBeforeUnmount, watch, shallowRef } from "vue";
|
||||
import type { CompletionContext } from "@codemirror/autocomplete";
|
||||
import type { EditorView as EditorViewType } from "@codemirror/view";
|
||||
import type { Extension } from "@codemirror/state";
|
||||
import { resolveExecutableSql } from "@/lib/sqlExecutionTarget";
|
||||
import { formatSqlText, type SqlFormatDialect } from "@/lib/sqlFormatter";
|
||||
import { useConnectionStore } from "@/stores/connectionStore";
|
||||
import { useSettingsStore, type EditorTheme } from "@/stores/settingsStore";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import {
|
||||
buildSqlCompletionItemsFromContext,
|
||||
getSqlCompletionContext,
|
||||
} from "@/lib/sqlCompletion";
|
||||
import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
|
||||
|
||||
const props = defineProps<{
|
||||
modelValue: string;
|
||||
|
|
@ -35,53 +35,23 @@ const settingsStore = useSettingsStore();
|
|||
const MIN_FONT_SIZE = 10;
|
||||
const MAX_FONT_SIZE = 24;
|
||||
let editorViewModule: typeof import("@codemirror/view") | null = null;
|
||||
let fontSizeTheme: import("@codemirror/state").Compartment | null = null;
|
||||
let fontFamilyTheme: import("@codemirror/state").Compartment | null = null;
|
||||
let fontThemeComp: import("@codemirror/state").Compartment | null = null;
|
||||
let codeMirrorTheme: import("@codemirror/state").Compartment | null = null;
|
||||
|
||||
async function loadTheme(theme: EditorTheme): Promise<Extension> {
|
||||
switch (theme) {
|
||||
case "one-dark":
|
||||
return (await import("@codemirror/theme-one-dark")).oneDark;
|
||||
case "vscode-dark":
|
||||
return (await import("@uiw/codemirror-theme-vscode")).vscodeDark;
|
||||
case "vscode-light":
|
||||
return (await import("@uiw/codemirror-theme-vscode")).vscodeLight;
|
||||
case "nord":
|
||||
return (await import("@uiw/codemirror-theme-nord")).nord;
|
||||
case "okaidia":
|
||||
return (await import("@uiw/codemirror-theme-okaidia")).okaidia;
|
||||
case "material":
|
||||
return (await import("@uiw/codemirror-theme-material")).materialDark;
|
||||
case "duotone-light":
|
||||
return (await import("@uiw/codemirror-theme-duotone")).duotoneLight;
|
||||
case "duotone-dark":
|
||||
return (await import("@uiw/codemirror-theme-duotone")).duotoneDark;
|
||||
case "xcode":
|
||||
return (await import("@uiw/codemirror-theme-xcode")).xcodeLight;
|
||||
default:
|
||||
return (await import("@codemirror/theme-one-dark")).oneDark;
|
||||
}
|
||||
}
|
||||
|
||||
function fontTheme(EditorView: typeof import("@codemirror/view").EditorView, size: number, family: string) {
|
||||
return EditorView.theme({
|
||||
"&": { height: "100%", fontSize: `${size}px` },
|
||||
".cm-scroller": { overflow: "auto" },
|
||||
".cm-content": { fontFamily: family },
|
||||
});
|
||||
}
|
||||
|
||||
function setFontSize(size: number) {
|
||||
const next = Math.min(MAX_FONT_SIZE, Math.max(MIN_FONT_SIZE, size));
|
||||
const ss = settingsStore.editorSettings;
|
||||
ss.fontSize = next;
|
||||
settingsStore.updateEditorSettings({ fontSize: next });
|
||||
if (view.value && fontSizeTheme && fontFamilyTheme && editorViewModule) {
|
||||
if (view.value && fontThemeComp && editorViewModule) {
|
||||
view.value.dispatch({
|
||||
effects: [
|
||||
fontSizeTheme.reconfigure(fontTheme(editorViewModule.EditorView, next, ss.fontFamily)),
|
||||
fontFamilyTheme.reconfigure(fontTheme(editorViewModule.EditorView, next, ss.fontFamily)),
|
||||
fontThemeComp.reconfigure(
|
||||
editorFontTheme(editorViewModule.EditorView, next, ss.fontFamily, {
|
||||
fixedHeight: true,
|
||||
scrollable: true,
|
||||
})
|
||||
),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
|
@ -197,8 +167,7 @@ onMounted(async () => {
|
|||
import("@codemirror/autocomplete"),
|
||||
]);
|
||||
editorViewModule = { EditorView, keymap } as typeof import("@codemirror/view");
|
||||
fontSizeTheme = new Compartment();
|
||||
fontFamilyTheme = new Compartment();
|
||||
fontThemeComp = new Compartment();
|
||||
codeMirrorTheme = new Compartment();
|
||||
|
||||
const ss = settingsStore.editorSettings;
|
||||
|
|
@ -248,7 +217,7 @@ onMounted(async () => {
|
|||
},
|
||||
]);
|
||||
|
||||
const theme = await loadTheme(ss.theme);
|
||||
const theme = await loadEditorTheme(ss.theme);
|
||||
|
||||
const state = EditorState.create({
|
||||
doc: props.modelValue,
|
||||
|
|
@ -278,8 +247,12 @@ onMounted(async () => {
|
|||
emit("selectionChange", selectedSqlFromView(update.view));
|
||||
}
|
||||
}),
|
||||
fontSizeTheme.of(fontTheme(EditorView, ss.fontSize, ss.fontFamily)),
|
||||
fontFamilyTheme.of(fontTheme(EditorView, ss.fontSize, ss.fontFamily)),
|
||||
fontThemeComp.of(
|
||||
editorFontTheme(EditorView, ss.fontSize, ss.fontFamily, {
|
||||
fixedHeight: true,
|
||||
scrollable: true,
|
||||
})
|
||||
),
|
||||
EditorView.domEventHandlers({
|
||||
wheel(event) {
|
||||
if (!event.metaKey && !event.ctrlKey) return false;
|
||||
|
|
@ -317,13 +290,17 @@ watch(
|
|||
watch(
|
||||
() => settingsStore.editorSettings,
|
||||
async (ss) => {
|
||||
if (!view.value || !codeMirrorTheme || !fontFamilyTheme || !fontSizeTheme || !editorViewModule) return;
|
||||
const themeExt = await loadTheme(ss.theme);
|
||||
if (!view.value || !codeMirrorTheme || !fontThemeComp || !editorViewModule) return;
|
||||
const themeExt = await loadEditorTheme(ss.theme);
|
||||
view.value.dispatch({
|
||||
effects: [
|
||||
codeMirrorTheme.reconfigure(themeExt),
|
||||
fontFamilyTheme.reconfigure(fontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily)),
|
||||
fontSizeTheme.reconfigure(fontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily)),
|
||||
fontThemeComp.reconfigure(
|
||||
editorFontTheme(editorViewModule.EditorView, ss.fontSize, ss.fontFamily, {
|
||||
fixedHeight: true,
|
||||
scrollable: true,
|
||||
})
|
||||
),
|
||||
],
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
import type { Extension } from "@codemirror/state";
|
||||
import type { EditorTheme } from "@/stores/settingsStore";
|
||||
|
||||
/** Load a CodeMirror theme extension by theme name. */
|
||||
export async function loadEditorTheme(theme: EditorTheme): Promise<Extension> {
|
||||
switch (theme) {
|
||||
case "one-dark":
|
||||
return (await import("@codemirror/theme-one-dark")).oneDark;
|
||||
case "vscode-dark":
|
||||
return (await import("@uiw/codemirror-theme-vscode")).vscodeDark;
|
||||
case "vscode-light":
|
||||
return (await import("@uiw/codemirror-theme-vscode")).vscodeLight;
|
||||
case "nord":
|
||||
return (await import("@uiw/codemirror-theme-nord")).nord;
|
||||
case "okaidia":
|
||||
return (await import("@uiw/codemirror-theme-okaidia")).okaidia;
|
||||
case "material":
|
||||
return (await import("@uiw/codemirror-theme-material")).materialDark;
|
||||
case "duotone-light":
|
||||
return (await import("@uiw/codemirror-theme-duotone")).duotoneLight;
|
||||
case "duotone-dark":
|
||||
return (await import("@uiw/codemirror-theme-duotone")).duotoneDark;
|
||||
case "xcode":
|
||||
return (await import("@uiw/codemirror-theme-xcode")).xcodeLight;
|
||||
default:
|
||||
return (await import("@codemirror/theme-one-dark")).oneDark;
|
||||
}
|
||||
}
|
||||
|
||||
/** Build a CodeMirror theme extension for font size + font family. */
|
||||
export function editorFontTheme(
|
||||
EditorView: typeof import("@codemirror/view").EditorView,
|
||||
size: number,
|
||||
family: string,
|
||||
opts?: { fixedHeight?: boolean; scrollable?: boolean },
|
||||
): Extension {
|
||||
return EditorView.theme({
|
||||
"&": {
|
||||
...(opts?.fixedHeight ? { height: "100%" } : {}),
|
||||
fontSize: `${size}px`,
|
||||
},
|
||||
...(opts?.scrollable ? { ".cm-scroller": { overflow: "auto" } } : {}),
|
||||
".cm-content": { fontFamily: family },
|
||||
});
|
||||
}
|
||||
|
|
@ -49,15 +49,17 @@ export const FONT_FAMILIES: { value: string; label: string }[] = [
|
|||
{ value: "monospace", label: "System Monospace" },
|
||||
];
|
||||
|
||||
const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
|
||||
fontFamily: "'JetBrains Mono', 'Fira Code', monospace",
|
||||
fontSize: 13,
|
||||
theme: "one-dark",
|
||||
};
|
||||
|
||||
export const STORAGE_KEY = "dbx-editor-settings";
|
||||
const OLD_FONT_SIZE_KEY = "dbx-query-editor-font-size";
|
||||
|
||||
function loadEditorSettings(): EditorSettings {
|
||||
// Try new format first
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (raw) {
|
||||
|
|
@ -69,6 +71,24 @@ function loadEditorSettings(): EditorSettings {
|
|||
};
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
// Migrate old font-size key if new settings don't exist
|
||||
try {
|
||||
const oldSize = localStorage.getItem(OLD_FONT_SIZE_KEY);
|
||||
if (oldSize) {
|
||||
const parsed = parseInt(oldSize, 10);
|
||||
if (!isNaN(parsed)) {
|
||||
const migrated: EditorSettings = {
|
||||
...DEFAULT_EDITOR_SETTINGS,
|
||||
fontSize: parsed,
|
||||
};
|
||||
saveEditorSettings(migrated);
|
||||
localStorage.removeItem(OLD_FONT_SIZE_KEY);
|
||||
return migrated;
|
||||
}
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
|
||||
return { ...DEFAULT_EDITOR_SETTINGS };
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue