mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-07-12 16:02:48 +08:00
🐛 fix(ui): 修复旧版编辑器布局并迁移默认界面至 V2
- 修复旧版 SQL 编辑器在弹性布局中的遮挡问题 - 将既有用户的界面偏好一次性迁移至 V2 并保留后续手动选择 - 覆盖持久化迁移与旧版编辑器布局回归测试
This commit is contained in:
@@ -239,6 +239,20 @@ body[data-theme='light'] ::-webkit-scrollbar-thumb:hover {
|
||||
background-clip: content-box;
|
||||
}
|
||||
|
||||
.gn-query-monaco-stage {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gn-query-monaco-shell {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.gn-query-monaco-stage .monaco-editor .suggest-details-container {
|
||||
min-height: 260px;
|
||||
}
|
||||
|
||||
13
frontend/src/components/QueryEditor.legacy-layout.test.ts
Normal file
13
frontend/src/components/QueryEditor.legacy-layout.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
const queryEditorSource = readFileSync(new URL('./QueryEditor.tsx', import.meta.url), 'utf8');
|
||||
const appCss = readFileSync(new URL('../App.css', import.meta.url), 'utf8');
|
||||
|
||||
describe('QueryEditor legacy layout', () => {
|
||||
it('keeps the legacy Monaco editor inside a flexing stage and shell', () => {
|
||||
expect(queryEditorSource).toContain("className={isV2Ui ? 'gn-v2-query-monaco-shell gn-query-monaco-shell' : 'gn-query-monaco-shell'}");
|
||||
expect(appCss).toMatch(/\.gn-query-monaco-stage\s*\{[\s\S]*?position:\s*relative;[\s\S]*?display:\s*flex;[\s\S]*?flex-direction:\s*column;[\s\S]*?min-height:\s*0;[\s\S]*?overflow:\s*hidden;/);
|
||||
expect(appCss).toMatch(/\.gn-query-monaco-shell\s*\{[\s\S]*?flex:\s*1 1 auto;[\s\S]*?min-height:\s*0;[\s\S]*?min-width:\s*0;/);
|
||||
});
|
||||
});
|
||||
@@ -8022,8 +8022,8 @@ const QueryEditor: React.FC<{ tab: TabData; isActive?: boolean }> = ({ tab, isAc
|
||||
>
|
||||
<div
|
||||
ref={editorShellRef}
|
||||
className={isV2Ui ? 'gn-v2-query-monaco-shell' : undefined}
|
||||
style={isV2Ui ? { flex: '1 1 auto', minHeight: 0 } : undefined}
|
||||
className={isV2Ui ? 'gn-v2-query-monaco-shell gn-query-monaco-shell' : 'gn-query-monaco-shell'}
|
||||
style={{ flex: '1 1 auto', minHeight: 0, minWidth: 0 }}
|
||||
>
|
||||
<Editor
|
||||
height="100%"
|
||||
|
||||
@@ -65,7 +65,7 @@ describe('store appearance persistence', () => {
|
||||
const { useStore } = await importStore();
|
||||
const appearance = useStore.getState().appearance;
|
||||
|
||||
expect(appearance.uiVersion).toBe('legacy');
|
||||
expect(appearance.uiVersion).toBe('v2');
|
||||
expect(appearance.enabled).toBe(false);
|
||||
expect(appearance.opacity).toBe(0.75);
|
||||
expect(appearance.blur).toBe(6);
|
||||
@@ -92,6 +92,38 @@ describe('store appearance persistence', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('migrates an existing legacy UI selection to V2', async () => {
|
||||
storage.setItem('lite-db-storage', JSON.stringify({
|
||||
state: {
|
||||
appearance: {
|
||||
uiVersion: 'legacy',
|
||||
},
|
||||
},
|
||||
version: 13,
|
||||
}));
|
||||
|
||||
const { useStore } = await importStore();
|
||||
expect(useStore.getState().appearance.uiVersion).toBe('v2');
|
||||
|
||||
const persisted = JSON.parse(storage.getItem('lite-db-storage') || '{}');
|
||||
expect(persisted.version).toBe(14);
|
||||
expect(persisted.state.appearance.uiVersion).toBe('v2');
|
||||
});
|
||||
|
||||
it('keeps a legacy UI selection made after the V2 migration', async () => {
|
||||
storage.setItem('lite-db-storage', JSON.stringify({
|
||||
state: {
|
||||
appearance: {
|
||||
uiVersion: 'legacy',
|
||||
},
|
||||
},
|
||||
version: 14,
|
||||
}));
|
||||
|
||||
const { useStore } = await importStore();
|
||||
expect(useStore.getState().appearance.uiVersion).toBe('legacy');
|
||||
});
|
||||
|
||||
it('persists DataGrid appearance settings and restores them after reload', async () => {
|
||||
const { useStore } = await importStore();
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ export const MIN_V2_SIDEBAR_RAIL_SCALE = 1.0;
|
||||
export const MAX_V2_SIDEBAR_RAIL_SCALE = 1.8;
|
||||
|
||||
export const DEFAULT_APPEARANCE: AppearanceSettings = {
|
||||
uiVersion: "legacy",
|
||||
uiVersion: "v2",
|
||||
enabled: true,
|
||||
opacity: 1.0,
|
||||
blur: 0,
|
||||
@@ -217,7 +217,8 @@ const MIN_KEEPALIVE_INTERVAL_MINUTES = 1;
|
||||
const MAX_KEEPALIVE_INTERVAL_MINUTES = 1440;
|
||||
const DEFAULT_DIAGNOSTIC_TIMEOUT_SECONDS = 15;
|
||||
const MAX_DIAGNOSTIC_TIMEOUT_SECONDS = 300;
|
||||
const PERSIST_VERSION = 13;
|
||||
const PERSIST_VERSION = 14;
|
||||
const UI_VERSION_V2_MIGRATION_VERSION = 14;
|
||||
const PERSIST_STORAGE_KEY = "lite-db-storage";
|
||||
const PERSIST_WRITE_DEBOUNCE_MS = 160;
|
||||
const MAX_PERSISTED_QUERY_TABS = 20;
|
||||
@@ -4505,7 +4506,12 @@ export const useStore = create<AppState>()(
|
||||
) as Partial<AppState>;
|
||||
captureLegacySavedQueriesSnapshot(state.savedQueries, state.connections);
|
||||
const nextState: Partial<AppState> = { ...state };
|
||||
nextState.connections = sanitizeConnections(state.connections);
|
||||
// 缺失连接表示由启动阶段从后端加载,迁移时不能把它写成空数组,
|
||||
// 否则会让持久化的侧栏根节点顺序提前丢失。
|
||||
nextState.connections =
|
||||
state.connections === undefined
|
||||
? undefined
|
||||
: sanitizeConnections(state.connections);
|
||||
const safeTabs = sanitizeQueryTabs(state.tabs);
|
||||
nextState.tabs = safeTabs;
|
||||
nextState.activeTabId = sanitizeActiveTabId(state.activeTabId, safeTabs);
|
||||
@@ -4535,7 +4541,13 @@ export const useStore = create<AppState>()(
|
||||
nextState.languagePreference = sanitizeLanguagePreference(
|
||||
state.languagePreference,
|
||||
);
|
||||
nextState.appearance = sanitizeAppearance(state.appearance, version);
|
||||
const appearance = sanitizeAppearance(state.appearance, version);
|
||||
// 旧版界面在窄布局下可能遮挡 SQL 编辑器。仅在本次版本升级时
|
||||
// 将已保存的选择迁移至 V2,之后用户仍可自行切换并保留偏好。
|
||||
nextState.appearance =
|
||||
version < UI_VERSION_V2_MIGRATION_VERSION
|
||||
? { ...appearance, uiVersion: "v2" }
|
||||
: appearance;
|
||||
nextState.uiScale = sanitizeUiScale(state.uiScale);
|
||||
nextState.fontSize = sanitizeFontSize(state.fontSize);
|
||||
nextState.startupFullscreen = sanitizeStartupFullscreen(
|
||||
|
||||
Reference in New Issue
Block a user