diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 745f315..20afaa7 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useMemo, useCallback } from 'react'; +import React, { useState, useEffect, useMemo, useCallback, useRef } from 'react'; import { Layout, Button, ConfigProvider, theme, message, Modal, Spin, Slider, Progress, Switch, Input, InputNumber, Select, Segmented, Tooltip } from 'antd'; import zhCN from 'antd/locale/zh_CN'; import { PlusOutlined, ConsoleSqlOutlined, UploadOutlined, DownloadOutlined, CloudDownloadOutlined, BugOutlined, ToolOutlined, GlobalOutlined, InfoCircleOutlined, GithubOutlined, SkinOutlined, CheckOutlined, MinusOutlined, BorderOutlined, CloseOutlined, SettingOutlined, LinkOutlined, BgColorsOutlined, AppstoreOutlined, RobotOutlined, FolderOpenOutlined, HddOutlined, SafetyCertificateOutlined, SwitcherOutlined, CodeOutlined } from '@ant-design/icons'; @@ -105,6 +105,7 @@ const MIN_FONT_SIZE = 12; const MAX_FONT_SIZE = 20; const DEFAULT_UI_SCALE = 1.0; const DEFAULT_FONT_SIZE = 14; +const EMPTY_INSTALLED_FONT_FAMILIES: InstalledFontFamily[] = []; type SidebarResizeBounds = { minWidth: number; maxWidth: number }; type SidebarResizeDragState = SidebarResizeBounds & { startX: number; @@ -229,9 +230,11 @@ class AIPanelErrorBoundary extends React.Component< function App() { const [isModalOpen, setIsModalOpen] = useState(false); + const [isConnectionModalMounted, setIsConnectionModalMounted] = useState(false); const [isSyncModalOpen, setIsSyncModalOpen] = useState(false); const [isDriverModalOpen, setIsDriverModalOpen] = useState(false); const [editingConnection, setEditingConnection] = useState(null); + const connectionModalWarmupDoneRef = useRef(false); const windowState = useStore(state => state.windowState); const themeMode = useStore(state => state.theme); const setTheme = useStore(state => state.setTheme); @@ -2072,6 +2075,57 @@ function App() { const [isShortcutModalOpen, setIsShortcutModalOpen] = useState(false); const [isSnippetModalOpen, setIsSnippetModalOpen] = useState(false); const [capturingShortcutAction, setCapturingShortcutAction] = useState(null); + useEffect(() => { + if (!isThemeModalOpen || themeModalSection !== 'appearance') { + return; + } + if (hasLoadedInstalledFontsRef.current || isFontFamiliesLoading) { + return; + } + + let cancelled = false; + hasLoadedInstalledFontsRef.current = true; + setIsFontFamiliesLoading(true); + setFontFamiliesLoadError(null); + + ListInstalledFontFamilies() + .then((result) => { + if (cancelled) { + return; + } + if (!result?.success) { + throw new Error(String(result?.message || '加载系统字体失败')); + } + const nextFonts = Array.isArray(result?.data) + ? result.data + .map((item) => ({ + family: sanitizeFontFamilyInput((item as InstalledFontFamily | Record)?.family) || '', + path: typeof (item as InstalledFontFamily | Record)?.path === 'string' + ? String((item as InstalledFontFamily | Record).path) + : undefined, + })) + .filter((item) => item.family) + : EMPTY_INSTALLED_FONT_FAMILIES; + setInstalledFontFamilies(nextFonts); + }) + .catch((error) => { + if (cancelled) { + return; + } + hasLoadedInstalledFontsRef.current = false; + setFontFamiliesLoadError(String(error instanceof Error ? error.message : error || '加载系统字体失败')); + }) + .finally(() => { + if (!cancelled) { + setIsFontFamiliesLoading(false); + } + }); + + return () => { + cancelled = true; + }; + }, [isThemeModalOpen, themeModalSection]); + const shortcutConflictMap = useMemo(() => { const map: Partial> = {}; for (const action of SHORTCUT_ACTION_ORDER) { @@ -2294,15 +2348,35 @@ function App() { const handleCreateConnection = useCallback(() => { setSecurityUpdateRepairSource(null); setEditingConnection(null); + setIsConnectionModalMounted(true); setIsModalOpen(true); }, []); const handleEditConnection = (conn: SavedConnection) => { setSecurityUpdateRepairSource(null); setEditingConnection(conn); + setIsConnectionModalMounted(true); setIsModalOpen(true); }; + useEffect(() => { + if (connectionModalWarmupDoneRef.current) { + return; + } + connectionModalWarmupDoneRef.current = true; + const warmup = () => setIsConnectionModalMounted(true); + if (typeof window === 'undefined') { + warmup(); + return; + } + if (typeof window.requestIdleCallback === 'function') { + const idleId = window.requestIdleCallback(() => warmup(), { timeout: 1200 }); + return () => window.cancelIdleCallback?.(idleId); + } + const timerId = window.setTimeout(warmup, 300); + return () => window.clearTimeout(timerId); + }, []); + const handleConnectionSaved = useCallback(async (savedConnection: SavedConnection) => { if (!shouldRetrySecurityUpdateAfterRepairSave(securityUpdateRepairSource)) { return; @@ -2643,8 +2717,13 @@ function App() { document.body.style.color = darkMode ? '#ffffff' : '#000000'; document.body.setAttribute('data-theme', darkMode ? 'dark' : 'light'); document.body.setAttribute('data-ui-version', appearance.uiVersion); + document.body.setAttribute('data-platform', runtimePlatform || ''); document.body.style.fontSize = `${effectiveFontSize}px`; + document.body.style.setProperty('--gn-font-sans', resolvedUiFontFamily); + document.body.style.setProperty('--gn-font-mono', resolvedMonoFontFamily); document.documentElement.style.setProperty('--gonavi-font-size', `${effectiveFontSize}px`); + document.documentElement.style.setProperty('--gn-font-sans', resolvedUiFontFamily); + document.documentElement.style.setProperty('--gn-font-mono', resolvedMonoFontFamily); document.documentElement.style.setProperty('--gn-ui-scale', `${effectiveUiScale}`); document.documentElement.style.setProperty('--gn-font-size', `${effectiveFontSize}px`); document.documentElement.style.setProperty('--gn-font-size-sm', `${Math.max(10, Math.round(effectiveFontSize * 0.86))}px`); @@ -3389,7 +3468,7 @@ function App() { )} - {isModalOpen && ( + {isConnectionModalMounted && ( void; @@ -422,7 +421,7 @@ const AISettingsModal: React.FC = ({ open, onClose, darkMo
{matchedPreset.label} · - {p.model || '未选择模型'} + {p.model || '未选择模型'}
@@ -683,7 +682,7 @@ const AISettingsModal: React.FC = ({ open, onClose, darkMo
{promptText} @@ -718,7 +717,7 @@ const AISettingsModal: React.FC = ({ open, onClose, darkMo
{tool.icon}
-
+
{tool.name}
{tool.desc}
@@ -733,7 +732,7 @@ const AISettingsModal: React.FC = ({ open, onClose, darkMo
参数: - + {tool.params}
@@ -835,6 +834,3 @@ export default AISettingsModal; - - - diff --git a/frontend/src/components/ConnectionModal.tsx b/frontend/src/components/ConnectionModal.tsx index 194404b..6ada63a 100644 --- a/frontend/src/components/ConnectionModal.tsx +++ b/frontend/src/components/ConnectionModal.tsx @@ -7333,6 +7333,7 @@ const ConnectionModal: React.FC<{ wordBreak: "break-all", lineHeight: "20px", fontSize: 13, + fontFamily: "var(--gn-font-mono)", }} > {String(testResult?.message || "暂无失败日志")} diff --git a/frontend/src/components/DataGrid.tsx b/frontend/src/components/DataGrid.tsx index 0803e68..2fc595d 100644 --- a/frontend/src/components/DataGrid.tsx +++ b/frontend/src/components/DataGrid.tsx @@ -184,6 +184,7 @@ const DATE_TIME_CACHE_LIMIT = 2000; const TABLE_CELL_PREVIEW_MAX_CHARS = 240; const DATA_GRID_DISPLAY_RENDER_VERSION = Symbol('DATA_GRID_DISPLAY_RENDER_VERSION'); const DATA_GRID_VIRTUAL_EDIT_RENDER_VERSION = Symbol('DATA_GRID_VIRTUAL_EDIT_RENDER_VERSION'); +const DEFAULT_GRID_MONO_FONT_FAMILY = '"JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace'; const normalizedDateTimeCache = new Map(); const objectCellPreviewCache = new WeakMap(); const makeCellKey = (rowKey: string, colName: string) => `${rowKey}${CELL_KEY_SEP}${colName}`; @@ -3990,7 +3991,7 @@ const DataGrid: React.FC = ({ const computed = window.getComputedStyle(element); const weight = computed.fontWeight || '400'; const size = computed.fontSize || '13px'; - const family = computed.fontFamily || 'sans-serif'; + const family = computed.fontFamily || DEFAULT_GRID_MONO_FONT_FAMILY; font = `${weight} ${size} ${family}`; } return (text: string) => measureTextWidth(text, font); @@ -4001,7 +4002,7 @@ const DataGrid: React.FC = ({ if (displayColumnNames.length === 0 || displayData.length === 0) return; const sig = displayColumnNames.join(','); if (autoFitDoneRef.current === sig) return; - const font = `${densityParams.dataFontSize}px -apple-system, sans-serif`; + const font = `${densityParams.dataFontSize}px ${DEFAULT_GRID_MONO_FONT_FAMILY}`; const newWidths: Record = {}; displayColumnNames.forEach((key) => { const autoWidth = calculateAutoFitColumnWidth({ @@ -4039,8 +4040,8 @@ const DataGrid: React.FC = ({ const nextWidth = calculateAutoFitColumnWidth({ headerTexts, valueTexts: displayDataRef.current.slice(0, 200).map((row) => row?.[normalizedKey]), - measureHeaderText: buildAutoFitMeasurer(headerEl ?? null, `600 ${densityParams.dataFontSize}px -apple-system, sans-serif`), - measureCellText: buildAutoFitMeasurer(sampleCell ?? null, `400 ${densityParams.dataFontSize}px -apple-system, sans-serif`), + measureHeaderText: buildAutoFitMeasurer(headerEl ?? null, `600 ${densityParams.dataFontSize}px ${DEFAULT_GRID_MONO_FONT_FAMILY}`), + measureCellText: buildAutoFitMeasurer(sampleCell ?? null, `400 ${densityParams.dataFontSize}px ${DEFAULT_GRID_MONO_FONT_FAMILY}`), defaultWidth, minWidth: 80, maxWidth: Math.max(720, Math.floor(containerWidth * 0.85)), diff --git a/frontend/src/components/DataGridPreviewPanel.tsx b/frontend/src/components/DataGridPreviewPanel.tsx index 2b72b79..a681d5e 100644 --- a/frontend/src/components/DataGridPreviewPanel.tsx +++ b/frontend/src/components/DataGridPreviewPanel.tsx @@ -86,6 +86,7 @@ const DataGridPreviewPanel: React.FC = ({ {focusedCellInfo ? ( = ({ minimap: { enabled: false }, scrollBeyondLastLine: false, wordWrap: 'on', - fontSize: 13, tabSize: 2, automaticLayout: true, readOnly: !focusedCellWritable, diff --git a/frontend/src/components/DataGridV2DdlWorkspace.tsx b/frontend/src/components/DataGridV2DdlWorkspace.tsx index 9b659db..66a72d8 100644 --- a/frontend/src/components/DataGridV2DdlWorkspace.tsx +++ b/frontend/src/components/DataGridV2DdlWorkspace.tsx @@ -60,6 +60,7 @@ export const DataGridV2DdlView: React.FC = ({
= ({ minimap: { enabled: false }, scrollBeyondLastLine: false, wordWrap: 'off', - fontSize: 12, tabSize: 2, automaticLayout: true, }} diff --git a/frontend/src/components/DataSyncModal.tsx b/frontend/src/components/DataSyncModal.tsx index 4fcadc3..292e0d6 100644 --- a/frontend/src/components/DataSyncModal.tsx +++ b/frontend/src/components/DataSyncModal.tsx @@ -9,7 +9,6 @@ import { isMacLikePlatform, normalizeOpacityForPlatform, resolveAppearanceValues import { buildRpcConnectionConfig } from '../utils/connectionRpcConfig'; import { formatLocalDateTimeLiteral, normalizeTemporalLiteralText } from './dataGridCopyInsert'; import { buildDataSyncRequest, type SourceDatasetMode, validateDataSyncSelection } from './dataSyncRequest'; - const { Title, Text } = Typography; const { Step } = Steps; const { Option } = Select; @@ -1239,7 +1238,7 @@ const DataSyncModal: React.FC<{ open: boolean; onClose: () => void }> = ({ open, padding: 12, height: 300, overflowY: 'auto', - fontFamily: 'SFMono-Regular, ui-monospace, Menlo, Consolas, monospace' + fontFamily: 'var(--gn-font-mono)' }} > {syncLogs.map((item, i: number) =>
{renderSyncLogItem(item)}
)} diff --git a/frontend/src/components/DriverManagerModal.tsx b/frontend/src/components/DriverManagerModal.tsx index 6b707d7..4bdbbbf 100644 --- a/frontend/src/components/DriverManagerModal.tsx +++ b/frontend/src/components/DriverManagerModal.tsx @@ -1674,7 +1674,7 @@ const DriverManagerModal: React.FC<{ open: boolean; onClose: () => void; onOpenG ) : null} {activeDriverLogLines.length > 0 ? ( -
+            
               {activeDriverLogLines.join('\n')}
             
) : ( diff --git a/frontend/src/components/ImportPreviewModal.tsx b/frontend/src/components/ImportPreviewModal.tsx index e77aa6d..b3f4cc7 100644 --- a/frontend/src/components/ImportPreviewModal.tsx +++ b/frontend/src/components/ImportPreviewModal.tsx @@ -5,7 +5,6 @@ import { PreviewImportFile, ImportDataWithProgress } from '../../wailsjs/go/app/ import { EventsOn, EventsOff } from '../../wailsjs/runtime/runtime'; import { useStore } from '../store'; import { buildRpcConnectionConfig } from '../utils/connectionRpcConfig'; - interface ImportPreviewModalProps { visible: boolean; filePath: string; @@ -234,7 +233,7 @@ const ImportPreviewModal: React.FC = ({ borderRadius: 4, padding: 12, fontSize: 12, - fontFamily: 'monospace' + fontFamily: 'var(--gn-font-mono)' }}> {importResult.errorLogs.map((log: string, idx: number) => (
{log}
diff --git a/frontend/src/components/LogPanel.tsx b/frontend/src/components/LogPanel.tsx index 412417d..d8f270a 100644 --- a/frontend/src/components/LogPanel.tsx +++ b/frontend/src/components/LogPanel.tsx @@ -3,7 +3,6 @@ import { Table, Tag, Button, Tooltip, Empty } from 'antd'; import { ClearOutlined, CloseOutlined, BugOutlined, ClockCircleOutlined } from '@ant-design/icons'; import { useStore } from '../store'; import { normalizeOpacityForPlatform, resolveAppearanceValues } from '../utils/appearance'; - interface LogPanelProps { height: number; onClose: () => void; @@ -76,7 +75,7 @@ const LogPanel: React.FC = ({ height, onClose, onResizeStart }) = title: 'SQL / Message', dataIndex: 'sql', render: (text: string, record: any) => ( -
+
{text}
{record.message &&
{record.message}
} {record.affectedRows !== undefined &&
Affected: {record.affectedRows}
} diff --git a/frontend/src/components/MonacoEditor.tsx b/frontend/src/components/MonacoEditor.tsx index 6c5d46a..ff25ce7 100644 --- a/frontend/src/components/MonacoEditor.tsx +++ b/frontend/src/components/MonacoEditor.tsx @@ -1,8 +1,15 @@ -import React, { useCallback, useEffect, useState } from 'react'; +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 | null = null; let transparentThemesRegistered = false; @@ -60,8 +67,23 @@ const ensureMonacoConfigured = (): Promise => { return monacoConfiguredPromise; }; -const MonacoEditor: React.FC = ({ beforeMount, loading, ...props }) => { +interface MonacoEditorProps extends EditorProps { + gonaviTypography?: GonaviMonacoTypography; +} + +const MonacoEditor: React.FC = ({ + 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; @@ -89,6 +111,38 @@ const MonacoEditor: React.FC = ({ beforeMount, loading, ...props }) 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 (
= ({ beforeMount, loading, ...props }) ); } - return ; + return ( + + ); }; export default MonacoEditor; diff --git a/frontend/src/components/QueryEditor.tsx b/frontend/src/components/QueryEditor.tsx index 2eda355..530e4a6 100644 --- a/frontend/src/components/QueryEditor.tsx +++ b/frontend/src/components/QueryEditor.tsx @@ -2972,6 +2972,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
= ({ tab, isAc minimap: { enabled: false }, automaticLayout: true, scrollBeyondLastLine: false, - fontSize: 14 }} />
@@ -3076,7 +3076,7 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc 执行失败
-
+
{executionError}
diff --git a/frontend/src/components/RedisCommandEditor.tsx b/frontend/src/components/RedisCommandEditor.tsx index 0590557..c8a1d97 100644 --- a/frontend/src/components/RedisCommandEditor.tsx +++ b/frontend/src/components/RedisCommandEditor.tsx @@ -111,6 +111,7 @@ const RedisCommandEditor: React.FC = ({ connectionId, r const appearance = useStore(state => state.appearance); const connection = connections.find(c => c.id === connectionId); const darkMode = theme === 'dark'; + const isV2Ui = appearance.uiVersion === 'v2'; const resolvedAppearance = resolveAppearanceValues(appearance); const opacity = normalizeOpacityForPlatform(resolvedAppearance.opacity); const blur = normalizeBlurForPlatform(resolvedAppearance.blur); @@ -406,6 +407,7 @@ const RedisCommandEditor: React.FC = ({ connectionId, r
= ({ connectionId, r options={{ minimap: { enabled: false }, lineNumbers: 'on', - fontSize: 14, wordWrap: 'on', scrollBeyondLastLine: false, automaticLayout: true, @@ -469,8 +470,9 @@ const RedisCommandEditor: React.FC = ({ connectionId, r overflow: 'auto', background: darkMode ? '#111418' : '#f8fafc', color: darkMode ? '#d4d4d4' : '#0f172a', - fontFamily: '"Consolas", "Courier New", monospace', - fontSize: 13, + fontFamily: 'var(--gn-font-mono)', + fontSize: 'var(--gn-font-size-mono, 13px)', + lineHeight: '1.62', padding: 12, }} > diff --git a/frontend/src/components/RedisViewer.tsx b/frontend/src/components/RedisViewer.tsx index 3525895..3c358be 100644 --- a/frontend/src/components/RedisViewer.tsx +++ b/frontend/src/components/RedisViewer.tsx @@ -1012,6 +1012,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => {
= ({ connectionId, redisDB }) => { automaticLayout: true, folding: true, formatOnPaste: true, - fontFamily: isBinary ? 'monospace' : undefined }} />
@@ -1135,7 +1135,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => { {tooltipContent}
} styles={{ root: { maxWidth: 600 } }}> {text} @@ -1285,7 +1285,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => { {tooltipContent}} styles={{ root: { maxWidth: 600 } }}> {text} @@ -1411,7 +1411,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => { {tooltipContent}} styles={{ root: { maxWidth: 600 } }}> {text} @@ -1536,7 +1536,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => { {tooltipContent}} styles={{ root: { maxWidth: 600 } }}> {text} @@ -1723,7 +1723,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => { {tooltipContent}} styles={{ root: { maxWidth: 720 } }}> {text} @@ -1960,6 +1960,7 @@ const RedisViewer: React.FC = ({ connectionId, redisDB }) => { > = ({ connectionId, redisDB }) => { > - - {statusBadge} - {displayTitle} + + + {statusBadge} + {displayTitle} + + + + + + + + + + + + + - - - - - - - - - - - - - - - -
-
- - - - - - - - - -
+ + +
+
+ + + + + + + + +
); @@ -8384,7 +8388,7 @@ const Sidebar: React.FC<{
已执行:{sqlFileExecState.executed} 条 | 失败: 0 ? '#ff4d4f' : undefined }}>{sqlFileExecState.failed}
{sqlFileExecState.currentSQL && sqlFileExecState.status === 'running' && ( -
+
{sqlFileExecState.currentSQL}
)} diff --git a/frontend/src/components/SnippetSettingsModal.tsx b/frontend/src/components/SnippetSettingsModal.tsx index 4339ca5..a7ba44f 100644 --- a/frontend/src/components/SnippetSettingsModal.tsx +++ b/frontend/src/components/SnippetSettingsModal.tsx @@ -12,7 +12,6 @@ import type { SqlSnippet } from '../types'; import { useStore } from '../store'; import { BUILTIN_SNIPPET_MAP } from '../utils/sqlSnippetDefaults'; import type { OverlayWorkbenchTheme } from '../utils/overlayWorkbenchTheme'; - interface SnippetSettingsModalProps { open: boolean; onClose: () => void; @@ -163,7 +162,7 @@ export default function SnippetSettingsModal({ key: 'syntax', label: '片段语法说明', children: ( -
+
{'${1:占位符} 第一个 Tab 位,占位符为提示文字'}
{'${2:默认值} 第二个 Tab 位,默认值可直接确认'}
{'$0 最终光标位置'}
@@ -361,7 +360,7 @@ export default function SnippetSettingsModal({ style={{ flex: 1, minHeight: 120, - fontFamily: 'monospace', + fontFamily: 'var(--gn-font-mono)', fontSize: 13, resize: 'none', }} diff --git a/frontend/src/components/TableDesignerSqlPreview.tsx b/frontend/src/components/TableDesignerSqlPreview.tsx index a04ed51..4d1407a 100644 --- a/frontend/src/components/TableDesignerSqlPreview.tsx +++ b/frontend/src/components/TableDesignerSqlPreview.tsx @@ -1,6 +1,5 @@ import { useCallback, useEffect, useMemo, useRef } from 'react'; import Editor, { type BeforeMount, type OnMount } from './MonacoEditor'; - interface TableDesignerSqlPreviewProps { sql: string; darkMode?: boolean; @@ -25,7 +24,6 @@ export interface SqlChangeHighlight { const SQL_PREVIEW_LIGHT_THEME = 'gonavi-sql-preview-light'; const SQL_PREVIEW_DARK_THEME = 'gonavi-sql-preview-dark'; - const CHANGE_LINE_RULES: Array<{ kind: SqlChangeHighlightKind; label: string; @@ -231,7 +229,7 @@ const TableDesignerSqlPreview: React.FC = ({ onMount={handleEditorMount} options={{ automaticLayout: true, - fontFamily: '"JetBrains Mono", "Cascadia Code", Consolas, monospace', + fontFamily: 'var(--gn-font-mono)', fontSize: 13, lineNumbers: 'on', lineDecorationsWidth: 14, diff --git a/frontend/src/components/ai/AIMessageBubble.tsx b/frontend/src/components/ai/AIMessageBubble.tsx index 3cfad53..f7b8999 100644 --- a/frontend/src/components/ai/AIMessageBubble.tsx +++ b/frontend/src/components/ai/AIMessageBubble.tsx @@ -16,7 +16,6 @@ import { resolveJVMDiagnosticPlanTargetTabId, } from '../../utils/jvmDiagnosticPlan'; import { buildAIReadonlyPreviewSQL } from '../../utils/aiSqlLimit'; - // 🔧 性能优化:将 ReactMarkdown 包装为 Memo 组件并提取固定的 plugins const remarkPlugins = [remarkGfm]; @@ -92,11 +91,11 @@ const AIToolResultItem: React.FC<{ resultMsg: AIChatMessage, darkMode: boolean, > {toolExpanded ? : } - 探针执行结果 ({resultMsg.tool_name || 'unknown'}) + 探针执行结果 ({resultMsg.tool_name || 'unknown'}) {charCount > 0 ? `${charCount} 个字符` : '无数据'}
{toolExpanded && ( -
+
{resultMsg.content}
)} @@ -291,7 +290,7 @@ const AIBlockHashRender = ({ match, darkMode, overlayTheme, children, activeConn padding: '6px 12px', background: darkMode ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.04)', fontSize: 12, color: overlayTheme.mutedText }}> - {match[1]} + {match[1]}
{isSql && } {isSelectQuery && activeConnectionConfig && ( @@ -332,7 +331,7 @@ const AIBlockHashRender = ({ match, darkMode, overlayTheme, children, activeConn codeTagProps={{ style: { fontSize: '14px', - fontFamily: 'Menlo, Monaco, Consolas, "Courier New", monospace' + fontFamily: 'var(--gn-font-mono)' } }} > @@ -383,7 +382,7 @@ const AIBlockHashRender = ({ match, darkMode, overlayTheme, children, activeConn setPreviewExpanded(false)}>收起 ▴
- +
{previewCols.map(col => ( diff --git a/frontend/src/components/jvm/JVMDiagnosticHistory.tsx b/frontend/src/components/jvm/JVMDiagnosticHistory.tsx index 449c422..ebf0d6d 100644 --- a/frontend/src/components/jvm/JVMDiagnosticHistory.tsx +++ b/frontend/src/components/jvm/JVMDiagnosticHistory.tsx @@ -12,7 +12,6 @@ import { formatJVMDiagnosticPhaseLabel, formatJVMDiagnosticTransportLabel, } from "../../utils/jvmDiagnosticPresentation"; - const { Text } = Typography; type JVMDiagnosticHistoryProps = { @@ -62,7 +61,7 @@ const JVMDiagnosticHistory: React.FC = ({ style={{ whiteSpace: "pre-wrap", wordBreak: "break-word", - fontFamily: "SFMono-Regular, Menlo, Monaco, Consolas, monospace", + fontFamily: "var(--gn-font-mono)", }} > {record.command} diff --git a/frontend/src/components/jvm/JVMDiagnosticOutput.tsx b/frontend/src/components/jvm/JVMDiagnosticOutput.tsx index 514d2a3..2fd046b 100644 --- a/frontend/src/components/jvm/JVMDiagnosticOutput.tsx +++ b/frontend/src/components/jvm/JVMDiagnosticOutput.tsx @@ -7,7 +7,6 @@ import { formatJVMDiagnosticEventLabel, formatJVMDiagnosticPhaseLabel, } from "../../utils/jvmDiagnosticPresentation"; - const { Text } = Typography; type JVMDiagnosticOutputProps = { @@ -44,7 +43,7 @@ const JVMDiagnosticOutput: React.FC = ({ style={{ whiteSpace: "pre-wrap", wordBreak: "break-word", - fontFamily: "SFMono-Regular, Menlo, Monaco, Consolas, monospace", + fontFamily: "var(--gn-font-mono)", }} > {chunkTexts[index]} diff --git a/frontend/src/v2-theme.css b/frontend/src/v2-theme.css index b4231ea..b768ee3 100644 --- a/frontend/src/v2-theme.css +++ b/frontend/src/v2-theme.css @@ -19,8 +19,6 @@ File is self-contained — safe to import once at app entry. ────────────────────────────────────────────────────────────────────── */ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap'); - /* ─── Light tokens ─────────────────────────────────────── */ body[data-ui-version="v2"][data-theme="light"] { --gn-font-sans: "Inter", "PingFang SC", -apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", sans-serif; @@ -111,9 +109,15 @@ body[data-ui-version="v2"] { font-size: var(--gn-font-size, var(--gonavi-font-size, 14px)); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + font-synthesis: none; font-feature-settings: "cv11", "ss01"; } +body[data-ui-version="v2"][data-platform="darwin"] { + -webkit-font-smoothing: subpixel-antialiased; + -moz-osx-font-smoothing: auto; +} + body[data-ui-version="v2"] code, body[data-ui-version="v2"] pre, body[data-ui-version="v2"] kbd, @@ -183,8 +187,8 @@ body[data-ui-version="v2"] .ant-tabs-tab.ant-tabs-tab-active::after { } body[data-ui-version="v2"] .ant-tabs-tab .ant-tabs-tab-btn { - font-family: var(--gn-font-mono); - font-size: 12.5px; + font-family: var(--gn-font-sans); + font-size: var(--gn-font-size-sm, 12px); } body[data-ui-version="v2"] .ant-tabs-ink-bar { @@ -216,7 +220,7 @@ body[data-ui-version="v2"] .ant-tree .ant-tree-node-content-wrapper:hover { body[data-ui-version="v2"] .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected { background: var(--gn-bg-selected) !important; color: var(--gn-fg-1) !important; - font-weight: 600; + font-weight: 400; } /* table names — mono */ @@ -420,8 +424,8 @@ body[data-ui-version="v2"] .ant-table-tbody > tr.ant-table-row-selected > td { /* ─── Tags / Badges ────────────────────────────────────── */ body[data-ui-version="v2"] .ant-tag { - font-family: var(--gn-font-mono); - font-size: 10.5px !important; + font-family: var(--gn-font-sans); + font-size: var(--gn-font-size-xs, 10px) !important; font-weight: 600; padding: 1px 6px !important; border-radius: 4px !important; @@ -627,7 +631,7 @@ body[data-ui-version="v2"] .gn-v2-data-grid-alt-toolbar > div:first-child { body[data-ui-version="v2"] .gn-v2-data-grid-fields-head span, body[data-ui-version="v2"] .gn-v2-data-grid-alt-toolbar span { color: var(--gn-fg-5); - font-family: var(--gn-font-mono); + font-family: var(--gn-font-sans); font-size: 10.5px; font-weight: 650; } @@ -637,7 +641,7 @@ body[data-ui-version="v2"] .gn-v2-data-grid-alt-toolbar strong { max-width: 520px; overflow: hidden; color: var(--gn-fg-1); - font-family: var(--gn-font-mono); + font-family: var(--gn-font-sans); font-size: 13px; font-weight: 750; text-overflow: ellipsis; @@ -936,6 +940,15 @@ body[data-ui-version="v2"] .monaco-editor .margin { background-color: var(--gn-bg-panel) !important; } +body[data-ui-version="v2"] .monaco-editor, +body[data-ui-version="v2"] .monaco-editor .margin, +body[data-ui-version="v2"] .monaco-editor .view-lines, +body[data-ui-version="v2"] .monaco-editor .view-line, +body[data-ui-version="v2"] .monaco-editor textarea, +body[data-ui-version="v2"] .monaco-editor .inputarea { + font-family: var(--gn-font-mono) !important; +} + /* ─── Progress ────────────────────────────────────────── */ body[data-ui-version="v2"] .ant-progress-bg { background: var(--gn-accent) !important; @@ -1407,13 +1420,14 @@ body[data-ui-version="v2"] .gn-v2-sidebar-redesign { } body[data-ui-version="v2"] .gn-v2-connection-rail { - width: calc(54px * var(--gn-ui-scale, 1)); - flex: 0 0 calc(54px * var(--gn-ui-scale, 1)); + width: calc(38px * var(--gn-ui-scale, 1)); + flex: 0 0 calc(38px * var(--gn-ui-scale, 1)); display: flex; flex-direction: column; align-items: center; + justify-content: flex-start; gap: calc(4px * var(--gn-ui-scale, 1)); - padding: calc(12px * var(--gn-ui-scale, 1)) 0; + padding: calc(8px * var(--gn-ui-scale, 1)) 0; border-right: 0.5px solid var(--gn-br-1); background: transparent; } @@ -1458,32 +1472,31 @@ body[data-ui-version="v2"] .gn-v2-rail-group + .gn-v2-rail-group { body[data-ui-version="v2"] .gn-v2-rail-group-header { position: relative; - width: calc(38px * var(--gn-ui-scale, 1)); - height: calc(24px * var(--gn-ui-scale, 1)); + width: 100%; + min-height: calc(24px * var(--gn-ui-scale, 1)); overflow: visible; border: none; - border-radius: calc(7px * var(--gn-ui-scale, 1)); + border-radius: 0; display: flex; align-items: center; - justify-content: center; - padding: 0; - background: var(--gn-bg-panel); - color: var(--gn-fg-4); - box-shadow: inset 0 0 0 0.5px var(--gn-br-1); + justify-content: flex-start; + gap: 4px; + padding: 0 4px 0 6px; + background: transparent; + color: var(--gn-fg-5); + box-shadow: none; } body[data-ui-version="v2"] .gn-v2-rail-group-header:hover { - background: var(--gn-bg-hover); - color: var(--gn-fg-2); + background: transparent; + color: var(--gn-fg-3); } body[data-ui-version="v2"] .gn-v2-rail-group-chevron { - position: absolute; - left: 4px; - bottom: 3px; + position: static; display: inline-flex; color: var(--gn-fg-5); - font-size: 10px; + font-size: 9px; line-height: 1; transform: rotate(0deg); transition: transform 120ms ease; @@ -1493,16 +1506,15 @@ body[data-ui-version="v2"] .gn-v2-rail-group.is-collapsed .gn-v2-rail-group-chev transform: rotate(-90deg); } -body[data-ui-version="v2"] .gn-v2-rail-group-badge { - max-width: 26px; - overflow: hidden; - color: var(--gn-fg-3); - font-family: var(--gn-font-mono); - font-size: 9px; - font-weight: 800; +body[data-ui-version="v2"] .gn-v2-rail-group-title { + min-width: 0; + color: var(--gn-fg-4); + font-size: 12px; + font-weight: 700; line-height: 1; - text-overflow: ellipsis; white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; } body[data-ui-version="v2"] .gn-v2-rail-group-count { @@ -1533,12 +1545,22 @@ body[data-ui-version="v2"] .gn-v2-rail-group-items { gap: calc(4px * var(--gn-ui-scale, 1)); } -body[data-ui-version="v2"] .gn-v2-rail-footer { - margin-top: auto; +body[data-ui-version="v2"] .gn-v2-rail-primary-actions, +body[data-ui-version="v2"] .gn-v2-rail-secondary-actions { + width: 100%; display: flex; flex-direction: column; align-items: center; - gap: calc(10px * var(--gn-ui-scale, 1)); + gap: calc(6px * var(--gn-ui-scale, 1)); +} + +body[data-ui-version="v2"] .gn-v2-rail-primary-actions { + padding-top: calc(4px * var(--gn-ui-scale, 1)); +} + +body[data-ui-version="v2"] .gn-v2-rail-secondary-actions { + margin-top: auto; + padding-bottom: calc(4px * var(--gn-ui-scale, 1)); } body[data-ui-version="v2"] .gn-v2-rail-action-group, @@ -1571,7 +1593,7 @@ body[data-ui-version="v2"] .gn-v2-rail-action:disabled { body[data-ui-version="v2"] .gn-v2-rail-item, body[data-ui-version="v2"] .gn-v2-rail-tool { position: relative; - width: calc(38px * var(--gn-ui-scale, 1)); + width: calc(36px * var(--gn-ui-scale, 1)); height: calc(38px * var(--gn-ui-scale, 1)); border-radius: calc(9px * var(--gn-ui-scale, 1)); border: none; @@ -1581,12 +1603,16 @@ body[data-ui-version="v2"] .gn-v2-rail-tool { display: flex; align-items: center; justify-content: center; + gap: 0; padding: 0; } body[data-ui-version="v2"] .gn-v2-rail-tool { + width: calc(24px * var(--gn-ui-scale, 1)); height: calc(32px * var(--gn-ui-scale, 1)); border-radius: calc(8px * var(--gn-ui-scale, 1)); + justify-content: center; + padding: 0; } body[data-ui-version="v2"] .gn-v2-rail-item:hover, @@ -1638,14 +1664,21 @@ body[data-ui-version="v2"] .gn-v2-rail-badge { letter-spacing: 0; } +body[data-ui-version="v2"] .gn-v2-rail-badge-wrap { + position: relative; + width: 22px; + height: 22px; + flex: 0 0 22px; +} + body[data-ui-version="v2"] .gn-v2-rail-fallback { display: none; } body[data-ui-version="v2"] .gn-v2-rail-status { position: absolute; - right: 3px; - bottom: 3px; + right: -2px; + bottom: -2px; width: 8px; height: 8px; border-radius: 50%; @@ -1661,22 +1694,53 @@ body[data-ui-version="v2"] .gn-v2-rail-status.is-error { background: var(--gn-danger); } -body[data-ui-version="v2"] .gn-v2-rail-add { +body[data-ui-version="v2"] .gn-v2-rail-create-button { margin-top: 4px; - border: 1.5px dashed var(--gn-br-2); - color: var(--gn-fg-4); + width: calc(40px * var(--gn-ui-scale, 1)); + height: calc(40px * var(--gn-ui-scale, 1)); + border: 1px dashed var(--gn-br-2); + border-radius: calc(14px * var(--gn-ui-scale, 1)); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 6px; + padding: 0; + color: var(--gn-fg-5); background: transparent; } +body[data-ui-version="v2"] .gn-v2-rail-create-button:hover { + background: color-mix(in srgb, var(--gn-bg-hover) 54%, transparent); +} + +body[data-ui-version="v2"] .gn-v2-rail-create-icon { + width: 24px; + height: 24px; + border-radius: 8px; + display: inline-flex; + align-items: center; + justify-content: center; + background: color-mix(in srgb, var(--gn-bg-panel) 72%, var(--gn-accent) 28%); + color: var(--gn-accent); + box-shadow: inset 0 0 0 0.5px color-mix(in srgb, var(--gn-accent) 28%, transparent); +} + +body[data-ui-version="v2"] .gn-v2-rail-create-label { + color: var(--gn-fg-5); + font-size: 10px; + line-height: 1; +} + body[data-ui-version="v2"] .gn-v2-object-explorer { min-width: 0; - flex: 1 1 calc(100% - 54px); + flex: 1 1 calc(100% - 38px); background: var(--gn-bg-panel-2); overflow: hidden; } body[data-ui-version="v2"] .gn-v2-active-connection-header { - padding: 12px 14px 10px; + padding: 10px 10px 8px 12px; min-height: 52px; border-bottom: 0.5px solid var(--gn-br-1); display: flex; @@ -1684,6 +1748,21 @@ body[data-ui-version="v2"] .gn-v2-active-connection-header { gap: 8px; } +body[data-ui-version="v2"] .gn-v2-active-connection-trigger { + min-width: 0; + flex: 1 1 auto; + height: 34px; + display: flex; + align-items: center; + gap: 8px; + padding: 0 9px; + border: 0; + border-radius: 0; + background: transparent; + color: inherit; + text-align: left; +} + body[data-ui-version="v2"] .gn-v2-live-dot { width: 7px; height: 7px; @@ -1703,7 +1782,7 @@ body[data-ui-version="v2"] .gn-v2-live-dot.is-error { body[data-ui-version="v2"] .gn-v2-active-connection-copy { min-width: 0; - flex: 1; + flex: 1 1 auto; } body[data-ui-version="v2"] .gn-v2-active-connection-copy strong, @@ -1713,23 +1792,32 @@ body[data-ui-version="v2"] .gn-v2-active-connection-copy span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + font-family: var(--gn-font-sans); + letter-spacing: 0; } body[data-ui-version="v2"] .gn-v2-active-connection-copy strong { color: var(--gn-fg-1); font-size: 13px; - font-weight: 700; + font-weight: 400; line-height: 1.2; } body[data-ui-version="v2"] .gn-v2-active-connection-copy span { margin-top: 2px; color: var(--gn-fg-4); - font-family: var(--gn-font-mono); - font-size: 10.5px; + font-size: 11px; + font-weight: 400; line-height: 1.2; } +body[data-ui-version="v2"] .gn-v2-active-connection-actions { + display: inline-flex; + align-items: center; + gap: 2px; + flex: 0 0 auto; +} + body[data-ui-version="v2"] .gn-v2-explorer-search { padding: 10px 10px 6px !important; border-bottom: none !important; @@ -2106,6 +2194,7 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-node-content-wra body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-node-content-wrapper.ant-tree-node-selected { background: var(--gn-bg-selected) !important; color: var(--gn-fg-1) !important; + font-weight: 400 !important; } body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-iconEle { @@ -2132,6 +2221,21 @@ body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-iconEle .anticon color: var(--gn-fg-4); } +body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .gn-v2-tree-folder-icon { + width: 22px; + height: 22px; + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 22px; +} + +body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .gn-v2-tree-folder-icon .anticon-folder, +body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .gn-v2-tree-folder-icon .anticon-folder-open { + color: #faad14; + font-size: 18px; +} + body[data-ui-version="v2"] .gn-v2-explorer-tree-shell .ant-tree-title { min-width: 0; display: inline-flex; @@ -2158,20 +2262,28 @@ body[data-ui-version="v2"] .gn-v2-tree-title { align-items: center; gap: 5px; color: var(--gn-fg-2); + font-family: var(--gn-font-sans); font-size: var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)); - font-weight: 650; + font-weight: 400 !important; + line-height: 1.25; } body[data-ui-version="v2"] .gn-v2-tree-title.is-mono .gn-v2-tree-label { - font-family: var(--gn-font-mono); - font-size: clamp(9px, calc(var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)) - 1px), 17px); + font-family: inherit; + font-size: inherit; + font-weight: 400 !important; } body[data-ui-version="v2"] .gn-v2-tree-title.is-group { - font-weight: 800; + font-weight: 400 !important; color: var(--gn-fg-1); } +body[data-ui-version="v2"] .gn-v2-tree-title.is-connection { + align-items: center; + gap: 6px; +} + body[data-ui-version="v2"] .gn-v2-tree-status { position: relative; margin: 0 2px 0 0; @@ -2210,15 +2322,25 @@ body[data-ui-version="v2"] .gn-v2-tree-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + letter-spacing: 0; + font-weight: 400 !important; +} + +body[data-ui-version="v2"] .gn-v2-tree-connection-copy { + min-width: 0; + flex: 1 1 auto; + color: var(--gn-fg-1); + font-weight: 400 !important; } body[data-ui-version="v2"] .gn-v2-tree-count { flex: 0 0 auto; color: var(--gn-fg-5); - font-family: var(--gn-font-mono); - font-size: clamp(9px, calc(var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)) - 2px), 16px); - font-weight: 650; - opacity: 0.78; + font-family: var(--gn-font-sans); + font-variant-numeric: tabular-nums; + font-size: clamp(10px, calc(var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)) - 1px), 16px); + font-weight: 400 !important; + opacity: 0.82; } body[data-ui-version="v2"] .gn-v2-table-pin-action { @@ -2259,19 +2381,40 @@ body[data-ui-version="v2"] .gn-v2-object-explorer .ant-tree .ant-tree-node-conte } body[data-ui-version="v2"] .gn-v2-tree-title[data-node-type="database"] { - font-weight: 800; + font-weight: 400 !important; color: var(--gn-fg-1); } body[data-ui-version="v2"] .gn-v2-tree-title[data-node-type="queries-folder"], body[data-ui-version="v2"] .gn-v2-tree-title[data-node-type="external-sql-root"] { - color: var(--gn-fg-4); - font-weight: 650; + color: var(--gn-fg-1); + font-weight: 400 !important; } -body[data-ui-version="v2"] .gn-v2-tree-title[data-group-key="views"], -body[data-ui-version="v2"] .gn-v2-tree-title[data-group-key="routines"] { - color: var(--gn-fg-4); +body[data-ui-version="v2"] .gn-v2-tree-external-root { + width: 100%; + min-width: 0; + display: inline-flex; + align-items: center; + justify-content: space-between; + gap: 8px; +} + +body[data-ui-version="v2"] .gn-v2-tree-external-root .gn-v2-tree-title { + min-width: 0; + flex: 1 1 auto; +} + +body[data-ui-version="v2"] .gn-v2-tree-external-root .gn-v2-tree-label { + display: inline-flex; + align-items: center; + gap: 6px; +} + +body[data-ui-version="v2"] .gn-v2-tree-external-root-action { + flex: 0 0 auto; + padding-inline: 4px !important; + height: 20px !important; } body[data-ui-version="v2"] .gn-v2-tree-title[data-group-key="triggers"] { @@ -2285,9 +2428,10 @@ body[data-ui-version="v2"] .gn-v2-tree-section-title { height: 22px; padding: 7px 4px 3px 2px; color: var(--gn-fg-5); - font-size: clamp(9px, calc(var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)) - 2px), 16px); - font-weight: 800; - line-height: 1; + font-family: var(--gn-font-sans); + font-size: clamp(10px, calc(var(--gn-sidebar-tree-font-size, var(--gn-font-size-sm, 12px)) - 1px), 16px); + font-weight: 400; + line-height: 1.1; letter-spacing: 0; pointer-events: none; user-select: none; @@ -2333,7 +2477,7 @@ body[data-ui-version="v2"] .gn-v2-sidebar-log-button { gap: 6px; padding: 0 10px; font-size: 12.5px; - font-weight: 700; + font-weight: 400; } body[data-ui-version="v2"] .gn-v2-sidebar-log-button:hover { @@ -2345,7 +2489,7 @@ body[data-ui-version="v2"] .gn-v2-sidebar-log-button small { color: var(--gn-fg-4); font-family: var(--gn-font-mono); font-size: 10.5px; - font-weight: 650; + font-weight: 400; } body[data-ui-version="v2"] .gn-v2-empty-workbench { @@ -2424,6 +2568,7 @@ body[data-ui-version="v2"] .gn-v2-empty-panel { display: flex; flex-direction: column; gap: 10px; + font-family: var(--gn-font-sans); } body[data-ui-version="v2"] .gn-v2-panel-heading { @@ -2431,8 +2576,11 @@ body[data-ui-version="v2"] .gn-v2-panel-heading { justify-content: space-between; align-items: center; color: var(--gn-fg-1); + font-family: var(--gn-font-sans); font-size: 13px; font-weight: 700; + line-height: 1.3; + letter-spacing: 0; margin-bottom: 4px; } @@ -2449,6 +2597,7 @@ body[data-ui-version="v2"] .gn-v2-empty-panel button { background: var(--gn-bg-panel-2); color: var(--gn-fg-1); cursor: pointer; + font-family: var(--gn-font-sans); } body[data-ui-version="v2"] .gn-v2-empty-panel button:hover { @@ -2469,11 +2618,23 @@ body[data-ui-version="v2"] .gn-v2-empty-panel button > .anticon { body[data-ui-version="v2"] .gn-v2-empty-panel strong, body[data-ui-version="v2"] .gn-v2-empty-panel small { display: block; + font-family: var(--gn-font-sans); + letter-spacing: 0; +} + +body[data-ui-version="v2"] .gn-v2-empty-panel strong { + color: var(--gn-fg-1); + font-size: 13px; + font-weight: 650; + line-height: 1.35; } body[data-ui-version="v2"] .gn-v2-empty-panel small { color: var(--gn-fg-4); + font-size: 11px; margin-top: 3px; + font-weight: 500; + line-height: 1.45; } /* ─── Full V2 workbench shell: app / topbar / workspace ─ */ @@ -2764,7 +2925,7 @@ body[data-ui-version="v2"] .gn-v2-data-grid-toolbar-title strong { color: var(--gn-fg-1); font-family: var(--gn-font-mono); font-size: 13.5px; - font-weight: 750; + font-weight: 400; text-overflow: ellipsis; } @@ -2774,7 +2935,7 @@ body[data-ui-version="v2"] .gn-v2-data-grid-toolbar-title small { color: var(--gn-fg-4); font-family: var(--gn-font-mono); font-size: 10.5px; - font-weight: 600; + font-weight: 400; text-overflow: ellipsis; } @@ -2897,12 +3058,13 @@ body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead > tr > th { background: var(--gn-bg-panel-2) !important; color: var(--gn-fg-2) !important; font-family: var(--gn-font-mono) !important; + font-weight: 400 !important; vertical-align: top !important; } body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead .ant-table-column-title { font-size: var(--gn-data-table-font-size, var(--gn-font-size-mono, 12px)); - font-weight: 750; + font-weight: 400 !important; } body[data-ui-version="v2"] .gn-v2-data-grid .ant-table-thead .ant-table-column-sorters { @@ -3007,7 +3169,7 @@ body[data-ui-version="v2"] .gn-v2-column-title-name { color: var(--gn-fg-1); font-family: var(--gn-font-mono); font-size: var(--gn-font-size-sm, 12px); - font-weight: 750; + font-weight: 400 !important; text-overflow: ellipsis; white-space: nowrap; } @@ -3049,14 +3211,14 @@ body[data-ui-version="v2"] .gn-v2-column-title-type { color: var(--gn-fg-4); font-family: var(--gn-font-mono); font-size: var(--gn-font-size-xs, 10px); - font-weight: 600; + font-weight: 400 !important; } body[data-ui-version="v2"] .gn-v2-column-title-comment { color: var(--gn-fg-4); - font-family: var(--gn-font-sans); + font-family: var(--gn-font-mono); font-size: var(--gn-font-size-xs, 10px); - font-weight: 500; + font-weight: 400 !important; } body[data-ui-version="v2"] .gn-v2-data-grid .react-resizable-handle::after { @@ -4416,19 +4578,23 @@ body[data-ui-version="v2"] .gn-v2-table-overview-icon { body[data-ui-version="v2"] .gn-v2-table-overview-title { color: var(--gn-fg-1) !important; - font-family: var(--gn-font-mono); + font-family: var(--gn-font-sans); font-size: 14px !important; - font-weight: 750 !important; + font-weight: 700 !important; + letter-spacing: 0; } body[data-ui-version="v2"] .gn-v2-table-overview-summary { color: var(--gn-fg-4) !important; - font-family: var(--gn-font-mono); + font-family: var(--gn-font-sans); + font-weight: 500; } body[data-ui-version="v2"] .gn-v2-table-overview-summary strong { color: var(--gn-fg-2); - font-weight: 750; + font-family: var(--gn-font-mono); + font-weight: 600; + font-variant-numeric: tabular-nums; } body[data-ui-version="v2"] .gn-v2-table-overview-content {