mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-15 19:19:35 +08:00
- 统一新版界面字体变量在侧边栏、AI 面板、日志、DDL、Redis 与数据视图中的落地使用 - 调整 v2 左侧 rail 布局,将新建组、批量操作表、批量操作库、运行外部 SQL 文件、定位当前打开表迁移到顶部主操作区 - 收敛新版侧边栏树节点、连接信息、字段表头与字段描述的字体与字重表现 - 让 Monaco 编辑器、数据预览和代码展示区域跟随新的 UI/Mono 字体配置 - Refs #510
168 lines
4.7 KiB
TypeScript
168 lines
4.7 KiB
TypeScript
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
import Editor, { loader, type BeforeMount, type EditorProps } from '@monaco-editor/react';
|
|
import { useStore } from '../store';
|
|
import { sanitizeDataTableFontSize } from '../utils/dataGridDisplay';
|
|
import { DEFAULT_MONO_FONT_FAMILY } from '../utils/fontFamilies';
|
|
|
|
export type { BeforeMount, OnMount } from '@monaco-editor/react';
|
|
export type GonaviMonacoTypography = 'code' | 'data';
|
|
|
|
const DEFAULT_FONT_SIZE = 14;
|
|
const MIN_FONT_SIZE = 12;
|
|
const MAX_FONT_SIZE = 20;
|
|
let monacoConfiguredPromise: Promise<void> | null = null;
|
|
let transparentThemesRegistered = false;
|
|
|
|
const isTestRuntime = (): boolean => {
|
|
const env = (import.meta as unknown as { env?: Record<string, unknown> }).env || {};
|
|
return env.MODE === 'test' || env.VITEST === true || env.VITEST === 'true';
|
|
};
|
|
|
|
export const registerGonaviMonacoThemes: BeforeMount = (monaco) => {
|
|
if (transparentThemesRegistered) {
|
|
return;
|
|
}
|
|
|
|
monaco.editor.defineTheme('transparent-dark', {
|
|
base: 'vs-dark',
|
|
inherit: true,
|
|
rules: [],
|
|
colors: {
|
|
'editor.background': '#00000000',
|
|
'editor.lineHighlightBackground': '#ffffff10',
|
|
'editorGutter.background': '#00000000',
|
|
'editorStickyScroll.background': '#1e1e1e',
|
|
'editorStickyScrollHover.background': '#2a2a2a',
|
|
},
|
|
});
|
|
monaco.editor.defineTheme('transparent-light', {
|
|
base: 'vs',
|
|
inherit: true,
|
|
rules: [],
|
|
colors: {
|
|
'editor.background': '#00000000',
|
|
'editor.lineHighlightBackground': '#00000010',
|
|
'editorGutter.background': '#00000000',
|
|
'editorStickyScroll.background': '#ffffff',
|
|
'editorStickyScrollHover.background': '#f5f5f5',
|
|
},
|
|
});
|
|
|
|
transparentThemesRegistered = true;
|
|
};
|
|
|
|
const ensureMonacoConfigured = (): Promise<void> => {
|
|
if (isTestRuntime()) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (!monacoConfiguredPromise) {
|
|
monacoConfiguredPromise = import('monaco-editor/esm/nls.messages.zh-cn')
|
|
.then(() => import('monaco-editor'))
|
|
.then((monaco) => {
|
|
loader.config({ monaco });
|
|
});
|
|
}
|
|
|
|
return monacoConfiguredPromise;
|
|
};
|
|
|
|
interface MonacoEditorProps extends EditorProps {
|
|
gonaviTypography?: GonaviMonacoTypography;
|
|
}
|
|
|
|
const MonacoEditor: React.FC<MonacoEditorProps> = ({
|
|
beforeMount,
|
|
gonaviTypography = 'code',
|
|
loading,
|
|
options,
|
|
...props
|
|
}) => {
|
|
const [ready, setReady] = useState(isTestRuntime);
|
|
const uiVersion = useStore((state) => state.appearance.uiVersion);
|
|
const dataTableFontSize = useStore((state) => state.appearance.dataTableFontSize);
|
|
const dataTableFontSizeFollowGlobal = useStore((state) => state.appearance.dataTableFontSizeFollowGlobal);
|
|
const monoFontFamily = useStore((state) => state.appearance.customMonoFontFamily);
|
|
const globalFontSize = useStore((state) => state.fontSize);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
void ensureMonacoConfigured()
|
|
.then(() => {
|
|
if (!cancelled) {
|
|
setReady(true);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error('Failed to configure Monaco Editor', error);
|
|
if (!cancelled) {
|
|
setReady(true);
|
|
}
|
|
});
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, []);
|
|
|
|
const handleBeforeMount: BeforeMount = useCallback((monaco) => {
|
|
registerGonaviMonacoThemes(monaco);
|
|
beforeMount?.(monaco);
|
|
}, [beforeMount]);
|
|
|
|
const resolvedOptions = useMemo(() => {
|
|
if (uiVersion !== 'v2') {
|
|
return options;
|
|
}
|
|
|
|
const effectiveGlobalFontSize = Math.min(
|
|
MAX_FONT_SIZE,
|
|
Math.max(MIN_FONT_SIZE, Math.round(Number(globalFontSize) || DEFAULT_FONT_SIZE)),
|
|
);
|
|
const effectiveDataTableFontSize = dataTableFontSizeFollowGlobal !== false
|
|
? effectiveGlobalFontSize
|
|
: (sanitizeDataTableFontSize(dataTableFontSize) ?? effectiveGlobalFontSize);
|
|
const resolvedFontSize = gonaviTypography === 'data'
|
|
? effectiveDataTableFontSize
|
|
: Math.max(10, Math.round(effectiveDataTableFontSize * 0.92));
|
|
|
|
return {
|
|
...options,
|
|
fontFamily: options?.fontFamily ?? monoFontFamily ?? DEFAULT_MONO_FONT_FAMILY,
|
|
fontSize: options?.fontSize ?? resolvedFontSize,
|
|
lineHeight: options?.lineHeight ?? Math.max(18, Math.round(resolvedFontSize * 1.62)),
|
|
};
|
|
}, [
|
|
dataTableFontSize,
|
|
dataTableFontSizeFollowGlobal,
|
|
globalFontSize,
|
|
gonaviTypography,
|
|
monoFontFamily,
|
|
options,
|
|
uiVersion,
|
|
]);
|
|
|
|
if (!ready) {
|
|
return (
|
|
<div
|
|
data-monaco-editor-loading="true"
|
|
style={{ height: props.height || '100%', width: props.width || '100%' }}
|
|
>
|
|
{loading || null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Editor
|
|
{...props}
|
|
options={resolvedOptions}
|
|
loading={loading}
|
|
beforeMount={handleBeforeMount}
|
|
/>
|
|
);
|
|
};
|
|
|
|
export default MonacoEditor;
|