From b22be5146bc0fe0dea9d9b60b0b607bf280c67cc Mon Sep 17 00:00:00 2001 From: mesalogo Date: Mon, 12 Oct 2026 21:26:39 +0800 Subject: [PATCH] feat(settings): add a scrollbar minimap switch under Appearance chatMinimapEnabled defaults to on; turning it off restores the native message scrollbar while keeping continuous history and DOM windowing. --- .../chat-history-navigation/README.md | 2 +- .../chat-history-navigation/progress.md | 7 +++++ src/main/application-settings-store.test.ts | 14 ++++++++++ src/main/application-settings-store.ts | 4 +++ src/renderer/src/App.test.tsx | 15 ++++++++++ src/renderer/src/App.tsx | 4 +++ src/renderer/src/ChatHistoryPane.test.tsx | 13 +++++++++ src/renderer/src/ChatHistoryPane.tsx | 6 +++- src/renderer/src/MagicNotesWorkspace.test.tsx | 1 + src/renderer/src/SettingsPanel.test.tsx | 28 +++++++++++++++++++ src/renderer/src/SettingsPanel.tsx | 20 +++++++++++++ .../src/UpdateSettingsSection.test.tsx | 1 + .../src/i18n/locales/en-US/settings.ts | 4 +++ .../src/i18n/locales/zh-CN/settings.ts | 4 +++ src/renderer/src/styles.css | 6 ++-- src/shared/application-settings-contracts.ts | 2 ++ src/shared/goodbuddy-config-contracts.test.ts | 1 + 17 files changed, 127 insertions(+), 5 deletions(-) diff --git a/docs/features/chat-history-navigation/README.md b/docs/features/chat-history-navigation/README.md index 8d087580..e8b1ab55 100644 --- a/docs/features/chat-history-navigation/README.md +++ b/docs/features/chat-history-navigation/README.md @@ -14,4 +14,4 @@ - **历史窗口**:允许滚动访问的消息范围。第一阶段扩大为当前已加载会话的全部消息。 - **DOM 窗口**:实际挂载消息组件的范围。继续由消息高度估算和最多约 60 条挂载消息控制。 -- **minimap**:替代消息区原生滚动条的 canvas 全局概览,高度与滚动区一致,悬停时展开为较宽的缩略长图;不等于完整渲染所有消息。 +- **minimap**:替代消息区原生滚动条的 canvas 全局概览,高度与滚动区一致,悬停时展开为较宽的缩略长图;不等于完整渲染所有消息。设置界面中称为“滚动条缩略图”(设置 › 外观),对应应用设置 `chatMinimapEnabled`,默认开启;关闭后恢复原生滚动条,历史窗口不受影响。 diff --git a/docs/features/chat-history-navigation/progress.md b/docs/features/chat-history-navigation/progress.md index 3f9e28a0..681863e9 100644 --- a/docs/features/chat-history-navigation/progress.md +++ b/docs/features/chat-history-navigation/progress.md @@ -73,3 +73,10 @@ - 验证:minimap、历史窗口、时间线定向测试 69/69(新增“行测量不��发重画”和“滑块对齐可见行的概览区段”用例);web 类型检查与 lint 通过;`npm run build:bundle` 通过。 - `npm run perf:app` 连续两次通过。第二次结果:长会话打开、长会话滚动、`stream-seeded`、`stream-and-type` 均无长任务,帧 p95 8.5 ms;24 次切换后 DOM 2,449、堆 23.2 MB,后半段增长 0.2 MB。第一次运行中长会话滚动帧 p95 为 16.6 ms,`stream-seeded` 有 1 个 62 ms 长任务,第二次未复现,按单次噪声记录。 - 未验证:真实桌面端滚动时的视觉稳定性需人工确认。 + +### 2026-10-12 外观设置开关 + +- 新增应用设置 `chatMinimapEnabled`(`z.boolean().default(true)`),旧设置文件缺省时读为开启,无需升级设置版本;`application.update` 配置工具复用同一更新 schema,自动接受该字段。 +- 设置 › 外观新增开关“滚动条缩略图”/“Scrollbar minimap”,位于“透明磨砂特效”之后,保存后立即生效。 +- 关闭时 `ChatHistoryPane` 不挂载 `ChatMinimap`,也不再设置 `data-minimap`,原生滚动条随之恢复;历史窗口(完整已加载消息)和约 60 条 DOM 窗口不变。 +- 验证:设置存储(缺省默认、两种取值的持久化与重载、非法值拒绝)、外观开关(中英文)、窗格开关切换用例均通过;相关 8 个测试文件 410 项中修正配置快照夹具后全部通过;`App.test.tsx` 342/350,8 个失败与此前相同;web/node 类型检查(除既有 `desktop-storage-electron-fixture.ts`)与改动文件 lint 通过。 diff --git a/src/main/application-settings-store.test.ts b/src/main/application-settings-store.test.ts index 9fe9b3bd..87b48bfb 100644 --- a/src/main/application-settings-store.test.ts +++ b/src/main/application-settings-store.test.ts @@ -84,6 +84,20 @@ describe('ApplicationSettingsStore', () => { expect(applicationSettingsSchema.parse({ ...defaultApplicationSettings, transparentFrostedEffectEnabled: undefined }).transparentFrostedEffectEnabled).toBe(true) }) + it('defaults a missing scrollbar minimap setting to on and persists both choices', async () => { + const { filePath, store } = await createStore() + const legacy: Record = { ...defaultApplicationSettings, version: 12, lastSeenReleaseNotesVersion: null } + delete legacy.chatMinimapEnabled + await writeFile(filePath, JSON.stringify(legacy)) + expect((await store.get()).chatMinimapEnabled).toBe(true) + for (const chatMinimapEnabled of [false, true]) { + expect((await store.update({ chatMinimapEnabled })).chatMinimapEnabled).toBe(chatMinimapEnabled) + await store.update({ checkUpdatesOnStartup: false }) + expect((await createApplicationSettingsStore(filePath).get()).chatMinimapEnabled).toBe(chatMinimapEnabled) + } + await expect(store.update({ chatMinimapEnabled: 'false' })).rejects.toThrow() + }) + it('persists and publishes both frosted glass choices across unrelated updates and reloads', async () => { const { filePath, store } = await createStore() expect((await store.get()).transparentFrostedEffectEnabled).toBe(true) diff --git a/src/main/application-settings-store.ts b/src/main/application-settings-store.ts index 4d918379..84c01b24 100644 --- a/src/main/application-settings-store.ts +++ b/src/main/application-settings-store.ts @@ -252,6 +252,7 @@ export const defaultApplicationSettings: ApplicationSettings = { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: true, + chatMinimapEnabled: true, updateSource: 'github', modelDownloadSource: 'modelscope', localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -295,6 +296,7 @@ export class ApplicationSettingsStore { | 'conversationHtmlRenderingEnabled' | 'desktopNotificationsEnabled' | 'transparentFrostedEffectEnabled' + | 'chatMinimapEnabled' | 'localToolEnvironment' | 'version' | 'applicationNavigation' @@ -532,6 +534,7 @@ export class ApplicationSettingsStore { checkUpdatesOnStartup: stored.checkUpdatesOnStartup, desktopNotificationsEnabled: stored.desktopNotificationsEnabled, transparentFrostedEffectEnabled: stored.transparentFrostedEffectEnabled, + chatMinimapEnabled: stored.chatMinimapEnabled, updateSource: stored.updateSource, modelDownloadSource: stored.modelDownloadSource, localToolEnvironment: stored.localToolEnvironment, @@ -581,6 +584,7 @@ export class ApplicationSettingsStore { checkUpdatesOnStartup: next.checkUpdatesOnStartup, desktopNotificationsEnabled: next.desktopNotificationsEnabled, transparentFrostedEffectEnabled: next.transparentFrostedEffectEnabled, + chatMinimapEnabled: next.chatMinimapEnabled, updateSource: next.updateSource, modelDownloadSource: next.modelDownloadSource, localToolEnvironment: next.localToolEnvironment, diff --git a/src/renderer/src/App.test.tsx b/src/renderer/src/App.test.tsx index 89264aed..dba4096a 100644 --- a/src/renderer/src/App.test.tsx +++ b/src/renderer/src/App.test.tsx @@ -1256,6 +1256,7 @@ function installRemoteProjectsSetting(enabled: boolean): { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github", modelDownloadSource: "modelscope", localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -3486,6 +3487,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -3591,6 +3593,7 @@ describe("App", () => { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -3608,6 +3611,7 @@ describe("App", () => { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -3711,6 +3715,7 @@ describe("App", () => { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -3728,6 +3733,7 @@ describe("App", () => { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -6430,6 +6436,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github", modelDownloadSource: "modelscope", localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14663,6 +14670,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14680,6 +14688,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14734,6 +14743,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14795,6 +14805,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14833,6 +14844,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14850,6 +14862,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github" as const, modelDownloadSource: "modelscope" as const, localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14913,6 +14926,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github", modelDownloadSource: "modelscope", localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -14973,6 +14987,7 @@ describe("App", () => { checkUpdatesOnStartup: false, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: "github", modelDownloadSource: "modelscope", localToolEnvironment: defaultLocalToolEnvironmentSettings, diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index dc520edd..a94e2e97 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -6511,6 +6511,8 @@ function App(): React.JSX.Element { const settingsPanelActions = useStableHandlers({ onTransparentFrostedEffectEnabledChange: (enabled: boolean) => updateApplicationSettings({ transparentFrostedEffectEnabled: enabled }), + onChatMinimapEnabledChange: (enabled: boolean) => + updateApplicationSettings({ chatMinimapEnabled: enabled }), onRetryApplicationSettings: () => void reloadApplicationSettings(), onBrandingPreferencesChange: (preferences: Parameters[0]) => { if (!saveBrandingPreferences(preferences)) { @@ -7069,6 +7071,7 @@ function App(): React.JSX.Element { conversationHtmlRenderingEnabled={ conversationHtmlRenderingEnabled } + chatMinimapEnabled={applicationSettings?.chatMinimapEnabled ?? true} conversationId={conversationId} key={conversationId} loadHistory={ensureConversationHistory} @@ -7394,6 +7397,7 @@ function App(): React.JSX.Element { {...settingsPanelActions} appearanceTheme={appearanceTheme} transparentFrostedEffectEnabled={applicationSettings?.transparentFrostedEffectEnabled ?? true} + chatMinimapEnabled={applicationSettings?.chatMinimapEnabled ?? true} applicationSettingsPending={applicationSettingsPending} applicationSettingsLocked={applicationSettingsUnconfirmed || !applicationSettings} applicationSettingsError={applicationSettingsError} diff --git a/src/renderer/src/ChatHistoryPane.test.tsx b/src/renderer/src/ChatHistoryPane.test.tsx index d5de394a..e76263f2 100644 --- a/src/renderer/src/ChatHistoryPane.test.tsx +++ b/src/renderer/src/ChatHistoryPane.test.tsx @@ -181,6 +181,19 @@ afterEach(() => { vi.unstubAllGlobals() }) +describe('ChatHistoryPane scrollbar minimap setting', () => { + it('replaces the native scrollbar only while the minimap is enabled', () => { + const messages = makeMessages(5) + const { container, rerender, props } = setup(messages) + const pane = container.querySelector('.chat-history-pane')! + expect(pane).toHaveAttribute('data-minimap', 'true') + expect(container.querySelector('.chat-minimap')).not.toBeNull() + rerender() + expect(pane).not.toHaveAttribute('data-minimap') + expect(container.querySelector('.chat-minimap')).toBeNull() + }) +}) + describe('ChatHistoryPane windowing', () => { it('restores disclosures after eviction by more than eight row interactions with bounded DOM', async () => { const messages = makeMessages(2_000).map(message => ({ ...message, role: 'assistant' as const, reasoning: 'Reasoning' })) diff --git a/src/renderer/src/ChatHistoryPane.tsx b/src/renderer/src/ChatHistoryPane.tsx index 5f2ce76a..d5976795 100644 --- a/src/renderer/src/ChatHistoryPane.tsx +++ b/src/renderer/src/ChatHistoryPane.tsx @@ -152,6 +152,7 @@ export const ChatHistoryPane = memo(function ChatHistoryPane({ active, artifactById, conversationHtmlRenderingEnabled, + chatMinimapEnabled = true, conversation, locale, onCopyMessage, @@ -177,6 +178,8 @@ export const ChatHistoryPane = memo(function ChatHistoryPane({ active: boolean; artifactById: ReadonlyMap; conversationHtmlRenderingEnabled: boolean; + /** Replaces the native scrollbar with the minimap; off restores the scrollbar. */ + chatMinimapEnabled?: boolean; conversation: Conversation; locale: TimeFormatLocale; onCopyMessage: (content: string, kind?: 'tool') => Promise; @@ -1071,6 +1074,7 @@ export const ChatHistoryPane = memo(function ChatHistoryPane({ className="chat-history-pane" data-active={active ? "true" : "false"} data-conversation-id={conversation.id} + data-minimap={chatMinimapEnabled ? "true" : undefined} hidden={!active} inert={!active} > @@ -1142,7 +1146,7 @@ export const ChatHistoryPane = memo(function ChatHistoryPane({ /> - {active && ( + {active && chatMinimapEnabled && ( { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: 'github', modelDownloadSource: 'modelscope', localToolEnvironment: defaultLocalToolEnvironmentSettings, @@ -1594,6 +1596,32 @@ describe('SettingsPanel runtime files', () => { expect(onChange).toHaveBeenLastCalledWith(true) }) + it.each([ + ['zh-CN', '外观', '滚动条缩略图'], + ['en-US', 'Appearance', 'Scrollbar minimap'] + ] as const)('exposes the scrollbar minimap Switch under Appearance in %s', async (locale, category, label) => { + await changeUiLocale(locale) + const onChange = vi.fn(async () => true) + const props = { + ...heartbeatSettingsProps, + open: true, + onClearLocalData: vi.fn(async () => {}), + onClose: vi.fn(), + onSaved: vi.fn(), + onChatMinimapEnabledChange: onChange + } + const { rerender } = render() + fireEvent.click(screen.getByRole('tab', { name: category })) + const toggle = screen.getByRole('switch', { name: label }) + expect(toggle).toBeChecked() + fireEvent.click(toggle) + expect(onChange).toHaveBeenCalledExactlyOnceWith(false) + rerender() + expect(toggle).not.toBeChecked() + fireEvent.click(toggle) + expect(onChange).toHaveBeenLastCalledWith(true) + }) + it.each([ ['zh-CN', '平台功能', '桌面通知'], ['en-US', 'Platform features', 'Desktop notifications'] diff --git a/src/renderer/src/SettingsPanel.tsx b/src/renderer/src/SettingsPanel.tsx index 77e4d4ce..0c7d7a56 100644 --- a/src/renderer/src/SettingsPanel.tsx +++ b/src/renderer/src/SettingsPanel.tsx @@ -243,6 +243,8 @@ type SettingsPanelProps = { applicationSettingsLocked?: boolean applicationSettingsError?: string onTransparentFrostedEffectEnabledChange?: (enabled: boolean) => Promise + chatMinimapEnabled?: boolean + onChatMinimapEnabledChange?: (enabled: boolean) => Promise onRetryApplicationSettings?: () => void onAppearanceThemeChange?: (theme: AppearanceTheme) => void brandingPreferences?: BrandingPreferences @@ -658,6 +660,8 @@ function SettingsPanelView({ applicationSettingsLocked = false, applicationSettingsError, onTransparentFrostedEffectEnabledChange, + chatMinimapEnabled = true, + onChatMinimapEnabledChange, onRetryApplicationSettings, onAppearanceThemeChange = () => {}, brandingPreferences = defaultBrandingPreferences, @@ -2380,6 +2384,22 @@ function SettingsPanelView({

{t('appearance.frostedGlass.description')}

+ +

+ {t('appearance.chatMinimap.description')} +

{applicationSettingsError && (

{applicationSettingsError}

diff --git a/src/renderer/src/UpdateSettingsSection.test.tsx b/src/renderer/src/UpdateSettingsSection.test.tsx index 9fa43d73..da5211d3 100644 --- a/src/renderer/src/UpdateSettingsSection.test.tsx +++ b/src/renderer/src/UpdateSettingsSection.test.tsx @@ -25,6 +25,7 @@ describe('UpdateSettingsSection', () => { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: 'github', modelDownloadSource: 'modelscope', localToolEnvironment: defaultLocalToolEnvironmentSettings, diff --git a/src/renderer/src/i18n/locales/en-US/settings.ts b/src/renderer/src/i18n/locales/en-US/settings.ts index 8f23dac6..596b20b6 100644 --- a/src/renderer/src/i18n/locales/en-US/settings.ts +++ b/src/renderer/src/i18n/locales/en-US/settings.ts @@ -876,6 +876,10 @@ export const settings = { description: 'Applies to the top bar and expanded runtime checklist. Takes effect as soon as it is saved.', retry: 'Retry' }, + chatMinimap: { + label: 'Scrollbar minimap', + description: 'Replaces the message scrollbar with an overview of the conversation that expands on hover; click or drag to jump. Takes effect as soon as it is saved.' + }, theme: { title: 'Interface theme', description: 'Applies immediately and is saved on this device', diff --git a/src/renderer/src/i18n/locales/zh-CN/settings.ts b/src/renderer/src/i18n/locales/zh-CN/settings.ts index 861dc0da..b1a5366d 100644 --- a/src/renderer/src/i18n/locales/zh-CN/settings.ts +++ b/src/renderer/src/i18n/locales/zh-CN/settings.ts @@ -811,6 +811,10 @@ export const settings = { description: '应用于顶栏和展开的运行待办卡片,保存后立即生效。', retry: '重试' }, + chatMinimap: { + label: '滚动条缩略图', + description: '在消息区右侧用缩略图代替滚动条,悬停时展开,可点击或拖动定位。保存后立即生效。' + }, theme: { title: '界面主题', description: '选择后立即应用,并保存在此设备', diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index 2e16d515..bf8bd829 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -7343,12 +7343,12 @@ button > svg { color: var(--danger); } -/* The minimap replaces the native scrollbar of the message scroller. */ -.chat-history-pane > .chat { +/* When enabled, the minimap replaces the native scrollbar of the message scroller. */ +.chat-history-pane[data-minimap='true'] > .chat { scrollbar-width: none; } -.chat-history-pane > .chat::-webkit-scrollbar { +.chat-history-pane[data-minimap='true'] > .chat::-webkit-scrollbar { display: none; } diff --git a/src/shared/application-settings-contracts.ts b/src/shared/application-settings-contracts.ts index 0dd91638..068688e5 100644 --- a/src/shared/application-settings-contracts.ts +++ b/src/shared/application-settings-contracts.ts @@ -60,6 +60,7 @@ const applicationPreferencesSchema = z checkUpdatesOnStartup: z.boolean(), desktopNotificationsEnabled: z.boolean().default(true), transparentFrostedEffectEnabled: z.boolean().default(true), + chatMinimapEnabled: z.boolean().default(true), updateSource: updateSourceSchema, modelDownloadSource: modelDownloadSourceSchema, localToolEnvironment: localToolEnvironmentSettingsSchema, @@ -94,6 +95,7 @@ export const applicationSettingsUpdateSchema = applicationPreferencesSchema applicationNavigation: applicationNavigationSchema.optional(), desktopNotificationsEnabled: z.boolean().optional(), transparentFrostedEffectEnabled: z.boolean().optional(), + chatMinimapEnabled: z.boolean().optional(), localInferenceEnabled: z.boolean().optional(), deviceSharingEnabled: z.boolean().optional(), magicNotesEnabled: z.boolean().optional(), diff --git a/src/shared/goodbuddy-config-contracts.test.ts b/src/shared/goodbuddy-config-contracts.test.ts index 7f5e91e5..c555df36 100644 --- a/src/shared/goodbuddy-config-contracts.test.ts +++ b/src/shared/goodbuddy-config-contracts.test.ts @@ -154,6 +154,7 @@ describe('GoodBuddy configuration contracts', () => { checkUpdatesOnStartup: true, desktopNotificationsEnabled: true, transparentFrostedEffectEnabled: false, + chatMinimapEnabled: true, updateSource: 'github', modelDownloadSource: 'modelscope', localToolEnvironment: defaultLocalToolEnvironmentSettings,