fix(home): default welcome page to workspace overview

Refs: #9115
This commit is contained in:
t8y2
2026-10-04 16:29:06 +08:00
parent e7a48bbcf5
commit 8a2241c36e
5 changed files with 64 additions and 10 deletions
+1 -1
View File
@@ -1450,7 +1450,7 @@ async function updateAllAvailable() {
}
}
const hasSqlFileConnections = computed(() => connectionStore.connections.some((c) => supportsSqlFileExecution(c.db_type)));
const welcomePageMode = computed(() => (settingsStore.isEditorSettingsLoaded ? settingsStore.editorSettings.welcomePageMode : "intro"));
const welcomePageMode = computed(() => (settingsStore.isEditorSettingsLoaded ? settingsStore.editorSettings.welcomePageMode : "workspace"));
const queryEditorDdlDatabaseType = computed(() => {
if (!queryEditorDdlTarget.value?.connectionId) return undefined;
return effectiveDatabaseTypeForConnection(connectionStore.getConfig(queryEditorDdlTarget.value.connectionId));
@@ -73,6 +73,7 @@ describe("EDITOR_SETTINGS_DRAFT_KEYS", () => {
it("includes the privacy-safe welcome page mode", () => {
expect(EDITOR_SETTINGS_DRAFT_KEYS).toContain("welcomePageMode");
expect(editorSettingsDraftFromSettings(makeSettings({ welcomePageMode: "intro" })).welcomePageMode).toBe("intro");
expect(editorSettingsDraftFromSettings(makeSettings({ welcomePageMode: "workspace" })).welcomePageMode).toBe("workspace");
});
+39
View File
@@ -18,6 +18,7 @@ import {
normalizeMcpGlobalPolicy,
type RightSidebarPanelState,
transitionRightSidebarPanels,
WELCOME_PAGE_DEFAULT_VERSION,
} from "@/stores/settingsStore";
import type { AiConfigItem } from "@/types/ai";
import { DATA_GRID_EXTRACTOR_OPTIONS_MIGRATION_VERSION } from "@/lib/dataGrid/dataGridCopyExtractor";
@@ -31,6 +32,16 @@ describe("normalizeEditorSettings", () => {
expect(normalizeEditorSettings({ resultTabPreferComments: "false" } as any).resultTabPreferComments).toBe(true);
});
it("defaults the welcome page to the workspace overview and honors an explicitly saved intro mode", () => {
expect(DEFAULT_EDITOR_SETTINGS.welcomePageMode).toBe("workspace");
expect(normalizeEditorSettings({}).welcomePageMode).toBe("workspace");
expect(normalizeEditorSettings({ welcomePageMode: "bogus", welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION }).welcomePageMode).toBe("workspace");
// A persisted intro from the intro-default builds carries no version marker and migrates back.
expect(normalizeEditorSettings({ welcomePageMode: "intro" }).welcomePageMode).toBe("workspace");
expect(normalizeEditorSettings({ welcomePageMode: "intro", welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION }).welcomePageMode).toBe("intro");
expect(normalizeEditorSettings({ welcomePageMode: "workspace", welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION }).welcomePageMode).toBe("workspace");
});
it("defaults and sanitizes AI conversation typography independently", () => {
expect(normalizeEditorSettings({})).toMatchObject({ aiFontFamily: "", aiFontSize: 12 });
expect(
@@ -1327,6 +1338,31 @@ describe("settingsStore persisted settings initialization", () => {
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ dataGridKeepFilterEditorExpanded: false }));
});
it("resets a persisted intro welcome page from the intro-default builds and keeps later explicit choices", async () => {
const loadEditorSettings = vi.fn().mockResolvedValueOnce({
welcomePageMode: "intro",
executeModeDefaultVersion: EXECUTE_MODE_CURRENT_DEFAULT_VERSION,
sidebarBrowseObjectsOnDatabaseActivationMigrationVersion: SIDEBAR_BROWSE_OBJECTS_MIGRATION_VERSION,
dataGridExtractorOptionsMigrationVersion: DATA_GRID_EXTRACTOR_OPTIONS_MIGRATION_VERSION,
});
const saveEditorSettings = vi.fn().mockResolvedValue(undefined);
vi.doMock("@/lib/backend/api", () => ({ loadEditorSettings, saveEditorSettings }));
const { useSettingsStore } = await import("@/stores/settingsStore");
const store = useSettingsStore();
await store.initEditorSettings();
expect(store.editorSettings.welcomePageMode).toBe("workspace");
expect(store.editorSettings.welcomePageModeDefaultVersion).toBe(WELCOME_PAGE_DEFAULT_VERSION);
await vi.waitFor(() => expect(saveEditorSettings).toHaveBeenCalledOnce());
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ welcomePageMode: "workspace", welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION }));
store.updateEditorSettings({ welcomePageMode: "intro" });
expect(store.editorSettings.welcomePageMode).toBe("intro");
await vi.waitFor(() => expect(saveEditorSettings).toHaveBeenCalledTimes(2));
expect(saveEditorSettings).toHaveBeenLastCalledWith(expect.objectContaining({ welcomePageMode: "intro", welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION }));
});
it("persists table completion schema qualification updates", async () => {
const loadEditorSettings = vi.fn().mockResolvedValue({});
const saveEditorSettings = vi.fn().mockResolvedValue(undefined);
@@ -1696,6 +1732,7 @@ describe("settingsStore editor settings persistence", () => {
ignoredUpdateVersion: "",
executeModeDefaultVersion: EXECUTE_MODE_CURRENT_DEFAULT_VERSION,
sidebarBrowseObjectsOnDatabaseActivationMigrationVersion: SIDEBAR_BROWSE_OBJECTS_MIGRATION_VERSION,
welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION,
});
const saveEditorSettings = vi.fn().mockRejectedValueOnce(new Error("save failed")).mockResolvedValueOnce(undefined);
vi.doMock("@/lib/backend/api", () => ({ loadEditorSettings, saveEditorSettings }));
@@ -1743,6 +1780,7 @@ describe("settingsStore editor settings persistence", () => {
ignoredUpdateVersion: "",
executeModeDefaultVersion: EXECUTE_MODE_CURRENT_DEFAULT_VERSION,
sidebarBrowseObjectsOnDatabaseActivationMigrationVersion: SIDEBAR_BROWSE_OBJECTS_MIGRATION_VERSION,
welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION,
});
const saveEditorSettings = vi.fn().mockImplementationOnce(
() =>
@@ -1779,6 +1817,7 @@ describe("settingsStore editor settings persistence", () => {
theme: "system",
executeModeDefaultVersion: EXECUTE_MODE_CURRENT_DEFAULT_VERSION,
sidebarBrowseObjectsOnDatabaseActivationMigrationVersion: SIDEBAR_BROWSE_OBJECTS_MIGRATION_VERSION,
welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION,
});
const saveEditorSettings = vi.fn().mockImplementationOnce(
() =>
+14 -4
View File
@@ -906,6 +906,7 @@ export interface EditorSettings {
appCloseUnsavedTabsMode: AppCloseUnsavedTabsMode;
savedSqlOpenTargetMode: SavedSqlOpenTargetMode;
welcomePageMode: WelcomePageMode;
welcomePageModeDefaultVersion: number;
compactTabTitle: boolean;
tabLayout: TabLayoutMode;
tabPlacement: TabPlacement;
@@ -1179,6 +1180,9 @@ const EDITOR_THEME_VALUES = new Set<EditorTheme>(EDITOR_THEMES.map((theme) => th
export const EXECUTE_MODE_CURRENT_DEFAULT_VERSION = 1;
export const SIDEBAR_BROWSE_OBJECTS_MIGRATION_VERSION = 1;
// v1: the welcome page default moved from "intro" back to "workspace"; persisted
// blobs from the intro-default builds lack this marker and are migrated.
export const WELCOME_PAGE_DEFAULT_VERSION = 1;
export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
fontFamily: DEFAULT_MONO_FONT_FAMILY,
@@ -1229,7 +1233,8 @@ export const DEFAULT_EDITOR_SETTINGS: EditorSettings = {
confirmUnsavedSqlClose: true,
appCloseUnsavedTabsMode: "keep-drafts",
savedSqlOpenTargetMode: "saved",
welcomePageMode: "intro",
welcomePageMode: "workspace",
welcomePageModeDefaultVersion: WELCOME_PAGE_DEFAULT_VERSION,
compactTabTitle: false,
tabLayout: "scroll",
tabPlacement: "top",
@@ -1722,6 +1727,9 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
const savedExecuteModeDefaultVersion = settings.executeModeDefaultVersion;
const executeModeDefaultVersion = typeof savedExecuteModeDefaultVersion === "number" && savedExecuteModeDefaultVersion >= EXECUTE_MODE_CURRENT_DEFAULT_VERSION ? savedExecuteModeDefaultVersion : EXECUTE_MODE_CURRENT_DEFAULT_VERSION;
const hasCurrentExecuteModeDefault = executeModeDefaultVersion === savedExecuteModeDefaultVersion;
const savedWelcomePageDefaultVersion = settings.welcomePageModeDefaultVersion;
const welcomePageModeDefaultVersion = typeof savedWelcomePageDefaultVersion === "number" && savedWelcomePageDefaultVersion >= WELCOME_PAGE_DEFAULT_VERSION ? savedWelcomePageDefaultVersion : WELCOME_PAGE_DEFAULT_VERSION;
const hasCurrentWelcomePageDefault = welcomePageModeDefaultVersion === savedWelcomePageDefaultVersion;
// The active id can only be validated once the scheme list it points into is known.
const dataGridTypeColorSchemes = normalizeDataGridTypeColorSchemes(settings.dataGridTypeColorSchemes);
const savedExtractorMigrationVersion = settings.dataGridExtractorOptionsMigrationVersion;
@@ -1825,7 +1833,8 @@ export function normalizeEditorSettings(settings: Partial<EditorSettings>, exist
confirmUnsavedSqlClose: settings.confirmUnsavedSqlClose ?? DEFAULT_EDITOR_SETTINGS.confirmUnsavedSqlClose,
appCloseUnsavedTabsMode: normalizeAppCloseUnsavedTabsMode(settings.appCloseUnsavedTabsMode),
savedSqlOpenTargetMode: settings.savedSqlOpenTargetMode === "current" ? "current" : DEFAULT_EDITOR_SETTINGS.savedSqlOpenTargetMode,
welcomePageMode: settings.welcomePageMode === "workspace" ? "workspace" : DEFAULT_EDITOR_SETTINGS.welcomePageMode,
welcomePageMode: hasCurrentWelcomePageDefault && (settings.welcomePageMode === "intro" || settings.welcomePageMode === "workspace") ? settings.welcomePageMode : DEFAULT_EDITOR_SETTINGS.welcomePageMode,
welcomePageModeDefaultVersion,
compactTabTitle: settings.compactTabTitle ?? DEFAULT_EDITOR_SETTINGS.compactTabTitle,
tabLayout: normalizeTabLayout(settings.tabLayout),
tabPlacement: normalizeTabPlacement(settings.tabPlacement),
@@ -2231,11 +2240,12 @@ export const useSettingsStore = defineStore("settings", () => {
query: typeof savedSettings.globalQueryTimeoutSecs === "number" || typeof (savedSettings as { queryTimeoutSecs?: unknown }).queryTimeoutSecs === "number",
};
const needsExecuteModeDefaultMigration = typeof savedSettings.executeModeDefaultVersion !== "number" || savedSettings.executeModeDefaultVersion < EXECUTE_MODE_CURRENT_DEFAULT_VERSION;
const needsWelcomePageDefaultMigration = typeof savedSettings.welcomePageModeDefaultVersion !== "number" || savedSettings.welcomePageModeDefaultVersion < WELCOME_PAGE_DEFAULT_VERSION;
const needsTabNavigationShortcutMigration = needsTabNavigationHistoryShortcutMigration(savedSettings.shortcuts);
const savedNullText = (savedSettings.dataGridExtractorOptions as Partial<DataGridExtractorOptions> | undefined)?.dsv?.nullText;
const needsDataGridExtractorOptionsMigration = (typeof savedSettings.dataGridExtractorOptionsMigrationVersion !== "number" || savedSettings.dataGridExtractorOptionsMigrationVersion < DATA_GRID_EXTRACTOR_OPTIONS_MIGRATION_VERSION) && savedNullText === "NULL";
const savedUpdateDownloadSource = (saved as { updateDownloadSource?: unknown }).updateDownloadSource;
if (savedUpdateDownloadSource === "atomgit" || needsExecuteModeDefaultMigration || needsTabNavigationShortcutMigration || needsSidebarBrowseObjectsMigration || needsDataGridExtractorOptionsMigration) {
if (savedUpdateDownloadSource === "atomgit" || needsExecuteModeDefaultMigration || needsWelcomePageDefaultMigration || needsTabNavigationShortcutMigration || needsSidebarBrowseObjectsMigration || needsDataGridExtractorOptionsMigration) {
// Persist one-time migrations so removed or unsafe defaults cannot reappear.
await enqueueEditorSettingsSave().catch(() => {});
}
@@ -2678,7 +2688,7 @@ export const useSettingsStore = defineStore("settings", () => {
if (partial.confirmUnsavedSqlClose !== undefined) editorSettings.value.confirmUnsavedSqlClose = partial.confirmUnsavedSqlClose;
if (partial.appCloseUnsavedTabsMode !== undefined) editorSettings.value.appCloseUnsavedTabsMode = normalizeAppCloseUnsavedTabsMode(partial.appCloseUnsavedTabsMode);
if (partial.savedSqlOpenTargetMode !== undefined) editorSettings.value.savedSqlOpenTargetMode = partial.savedSqlOpenTargetMode === "current" ? "current" : "saved";
if (partial.welcomePageMode !== undefined) editorSettings.value.welcomePageMode = partial.welcomePageMode === "workspace" ? "workspace" : DEFAULT_EDITOR_SETTINGS.welcomePageMode;
if (partial.welcomePageMode !== undefined) editorSettings.value.welcomePageMode = partial.welcomePageMode === "intro" || partial.welcomePageMode === "workspace" ? partial.welcomePageMode : DEFAULT_EDITOR_SETTINGS.welcomePageMode;
if (partial.compactTabTitle !== undefined) editorSettings.value.compactTabTitle = partial.compactTabTitle;
if (partial.tabLayout !== undefined) editorSettings.value.tabLayout = normalizeTabLayout(partial.tabLayout);
if (partial.tabPlacement !== undefined) editorSettings.value.tabPlacement = normalizeTabPlacement(partial.tabPlacement);
+9 -5
View File
@@ -4,20 +4,24 @@ DBX 在没有打开查询标签时,可以展示产品介绍或工作区概览
## 两种模式
- **DBX 介绍(默认)**:展示产品说明、连接管理、SQL 编辑、数据浏览与传输、AI / MCP 能力,以及新建连接、导入连接等固定入口。不展示个人连接名称、地址、数量、已连接状态、数据库类型统计或 SQL 历史;查询入口继续由现有工具栏提供。
- **工作区概览**:保留原有连接统计、快速连接和保存的 SQL 历史,适合希望快速回到日常工作的用户。
- **工作区概览(默认)**:保留原有连接统计、快速连接和保存的 SQL 历史,适合希望快速回到日常工作的用户。
- **DBX 介绍**:展示产品说明、连接管理、SQL 编辑、数据浏览与传输、AI / MCP 能力,以及新建连接、导入连接等固定入口。不展示个人连接名称、地址、数量、已连接状态、数据库类型统计或 SQL 历史;查询入口继续由现有工具栏提供。适合在展示、录屏或共用屏幕时不想露出个人数据的用户主动开启。
介绍页采用主题颜色、留白和响应式布局,兼容深浅主题。首页设置支持设置搜索、配置导入导出和恢复默认。模式值为 `welcomePageMode: "intro" | "workspace"`,缺失或无效配置回退为介绍页,因此升级用户与新用户都采用相同的默认行为。
介绍页采用主题颜色、留白和响应式布局,兼容深浅主题。首页设置支持设置搜索、配置导入导出和恢复默认。模式值为 `welcomePageMode: "intro" | "workspace"`,缺失或无效配置回退为工作区概览。
### 默认值迁移
介绍页曾在 v0.6.33 短暂作为默认值。当前版本通过 `welcomePageModeDefaultVersion` 标记把默认值迁回工作区概览:没有该标记的历史设置(包括 v0.6.33 期间被整体快照写入的 `intro`)在启动时一次性重置为工作区概览并落盘;标记为当前版本后,用户显式选择的介绍页会跨重启保留。
## 实现与边界
`App.vue` 在设置加载完成前保持介绍模式;介绍模式下首页统计、快速连接和 SQL 历史计算直接返回零值或空列表,不读取或排序这些个人数据。`WelcomeScreen.vue` 默认使用介绍模式,个人数据 props 可省略,介绍分支与工作区分支分别渲染。介绍页的按钮布局也不依赖是否已连接数据库。
`App.vue` 在设置加载完成前保持工作区模式;介绍模式下首页统计、快速连接和 SQL 历史计算直接返回零值或空列表,不读取或排序这些个人数据。`WelcomeScreen.vue` 个人数据 props 可省略,介绍分支与工作区分支分别渲染。介绍页的按钮布局也不依赖是否已连接数据库。
该选项只控制**中央空白首页**,不隐藏连接侧栏、已打开的标签、工具栏或用户主动打开的历史窗口,也不删除连接与历史数据。展示或录屏前如需隐藏整个工作区的个人信息,仍需检查这些区域;这项设置不是全局隐私模式。
## 验收重点
1. 无配置、旧配置和无效值均显示介绍页;保存工作区模式后重启恢复该选择。
1. 无配置、旧配置和无效值均显示工作区概览;保存介绍页模式后重启恢复该选择;v0.6.33 存量的 `intro`(无版本标记)首次启动被迁回工作区概览并持久化。
2. 介绍页不出现连接统计、名称、地址和 SQL 历史,连接或历史数据变化不改变介绍页内容。
3. 切换到工作区概览恢复原有统计与操作,设置取消不保存,应用后立即生效。
4. 外观恢复默认、全局恢复默认、导入导出与设置搜索正确覆盖该选项。