mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
@@ -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");
|
||||
});
|
||||
|
||||
|
||||
@@ -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(
|
||||
() =>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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. 外观恢复默认、全局恢复默认、导入导出与设置搜索正确覆盖该选项。
|
||||
|
||||
Reference in New Issue
Block a user