fix(settings): allow memory as the default session tab (#494)

This commit is contained in:
KokerZhou authored and GitHub committed 2026-10-12 20:50:43 +08:00
1 parent 7b791785b7
commit 0edc64d99a
13 files changed
+117 -18

No files matched your search

+11 -1
View File
@@ -138,7 +138,17 @@ export function writeConfigField(section: string, key: string, value: string | n
if (!existing[section] || typeof existing[section] !== 'object') {
existing[section] = {}
}
existing[section][key] = value
if (section === 'ui' && key === 'default_session_tab') {
if (value === 'memory') {
existing.ui.default_session_tab = 'overview'
existing.ui.default_session_tab_override = 'memory'
} else {
existing.ui.default_session_tab = value
delete existing.ui.default_session_tab_override
}
} else {
existing[section][key] = value
}
const lines: string[] = []
for (const [sec, entries] of Object.entries(existing)) {
+36
View File
@@ -14,8 +14,44 @@ test('normalizes the released overview preference to insights', () => {
assert.equal(config.ui.default_session_tab, 'insights')
})
test('accepts memory as a session landing tab', () => {
const config = configSchema.parse({
ui: {
default_session_tab: 'overview',
default_session_tab_override: 'memory',
},
})
assert.equal(config.ui.default_session_tab, 'memory')
})
test('accepts canonical memory input and its parsed output', () => {
const config = configSchema.parse({ ui: { default_session_tab: 'memory' } })
assert.equal(config.ui.default_session_tab, 'memory')
assert.equal(configSchema.parse(config).ui.default_session_tab, 'memory')
})
test('prefers a later legacy session tab change over the memory override', () => {
const config = configSchema.parse({
ui: {
default_session_tab: 'ai-chat',
default_session_tab_override: 'memory',
},
})
assert.equal(config.ui.default_session_tab, 'ai-chat')
})
test('does not reinterpret a historical overview preference as memory', () => {
const config = configSchema.parse({ ui: { default_session_tab: 'overview' } })
assert.equal(config.ui.default_session_tab, 'insights')
})
test('rejects unknown session landing tabs', () => {
assert.throws(() => configSchema.parse({ ui: { default_session_tab: 'unknown' } }))
assert.throws(() => configSchema.parse({ ui: { default_session_tab_override: 'unknown' } }))
})
test('defaults Windows close behavior to running in the background', () => {
+19 -8
View File
@@ -30,14 +30,25 @@ export const localeConfigSchema = z.object({
lang: z.string().default(''),
})
export const uiConfigSchema = z.object({
default_session_tab: z.preprocess(
(value) => (value === 'overview' ? 'insights' : value),
z.enum(['insights', 'ai-chat']).default('insights')
),
session_gap_threshold: z.number().int().min(60).max(86400).default(1800),
summary_strategy: z.enum(['brief', 'standard']).default('standard'),
})
const defaultSessionTabSchema = z.preprocess(
(value) => (value === 'overview' ? 'insights' : value),
z.enum(['insights', 'ai-chat', 'memory']).default('insights')
)
export const uiConfigSchema = z
.object({
default_session_tab: z.unknown().optional(),
default_session_tab_override: z.literal('memory').optional(),
session_gap_threshold: z.number().int().min(60).max(86400).default(1800),
summary_strategy: z.enum(['brief', 'standard']).default('standard'),
})
.transform(({ default_session_tab_override, ...config }) => ({
...config,
default_session_tab:
default_session_tab_override && config.default_session_tab === 'overview'
? default_session_tab_override
: defaultSessionTabSchema.parse(config.default_session_tab),
}))
export const cliConfigSchema = z.object({
/** Allow --raw output that bypasses privacy preprocessing (user-side opt-in). */
@@ -3,9 +3,12 @@ import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync, mkdirSync
import { join } from 'node:path'
import { tmpdir } from 'node:os'
import { after, beforeEach, describe, it } from 'node:test'
import { parse as parseToml } from 'smol-toml'
import { z } from 'zod'
const tempHome = mkdtempSync(join(tmpdir(), 'chatlab-config-set-'))
process.env.HOME = tempHome
process.env.USERPROFILE = tempHome
const [{ setConfigField, ConfigSetError }, { loadConfig }] = await Promise.all([
import('./set-config-field'),
@@ -69,6 +72,38 @@ describe('setConfigField', () => {
assert.equal(loadConfig().desktop.close_behavior, 'background')
})
it('persists memory without breaking the v0.37.5 UI config schema', () => {
setConfigField('ui.default_session_tab', 'memory')
assert.equal(loadConfig().ui.default_session_tab, 'memory')
const saved = parseToml(readFileSync(configToml, 'utf-8')) as { ui: Record<string, unknown> }
const releasedUiConfigSchema = z.object({
default_session_tab: z.preprocess(
(value) => (value === 'overview' ? 'insights' : value),
z.enum(['insights', 'ai-chat']).default('insights')
),
session_gap_threshold: z.number().int().min(60).max(86400).default(1800),
summary_strategy: z.enum(['brief', 'standard']).default('standard'),
})
assert.equal(releasedUiConfigSchema.parse(saved.ui).default_session_tab, 'insights')
})
it('honors a later session tab write from a v0.37.5 client', () => {
setConfigField('ui.default_session_tab', 'memory')
let content = readFileSync(configToml, 'utf-8').replace(
'default_session_tab = "overview"',
'default_session_tab = "ai-chat"'
)
writeFileSync(configToml, content, 'utf-8')
assert.equal(loadConfig().ui.default_session_tab, 'ai-chat')
content = content.replace('default_session_tab = "ai-chat"', 'default_session_tab = "insights"')
writeFileSync(configToml, content, 'utf-8')
assert.equal(loadConfig().ui.default_session_tab, 'insights')
})
it('writes boolean value and round-trips via loadConfig', () => {
const result = setConfigField('cli.allow_raw', 'true')
assert.deepEqual(result, { section: 'cli', key: 'allow_raw', value: true })
+1 -1
View File
@@ -1420,7 +1420,7 @@ export interface Preferences {
}
export interface UiConfig {
default_session_tab: 'insights' | 'ai-chat'
default_session_tab: 'insights' | 'ai-chat' | 'memory'
session_gap_threshold: number
summary_strategy?: 'brief' | 'standard'
}
@@ -11,7 +11,7 @@ import { availableLocales, type LocaleType } from '@/i18n'
import NetworkSettingsSection from './NetworkSettingsSection.vue'
import UITabs from '@/components/UI/Tabs.vue'
import { usePlatformService } from '@/services'
import { IS_ELECTRON } from '@/utils/platform'
import { IS_ELECTRON, IS_WEB_WASM } from '@/utils/platform'
import { INSIGHT_CARD_THEMES, type InsightCardThemeId } from '@/utils/insight-card-theme'
import type { DesktopCloseBehavior } from '@openchatlab/shared-types'
@@ -155,6 +155,7 @@ const currentLocale = computed({
const defaultTabOptions = computed(() => [
{ label: t('settings.basic.defaultTab.insights'), value: 'insights' },
{ label: t('settings.basic.defaultTab.aiChat'), value: 'ai-chat' },
...(!IS_WEB_WASM ? [{ label: t('settings.basic.defaultTab.memory'), value: 'memory' }] : []),
])
// Tools panel position options
+2 -1
View File
@@ -44,7 +44,8 @@
"defaultTab": {
"description": "Default page when entering a chat conversation",
"insights": "Insights",
"aiChat": "AI Chat"
"aiChat": "AI Chat",
"memory": "Memory"
},
"autoLaunch": {
"openAtLogin": "Launch at Login",
+2 -1
View File
@@ -44,7 +44,8 @@
"defaultTab": {
"description": "チャットに入った時に表示するページ",
"insights": "インサイト",
"aiChat": "AI チャット"
"aiChat": "AI チャット",
"memory": "メモリー"
},
"autoLaunch": {
"openAtLogin": "ログイン時に自動起動",
+2 -1
View File
@@ -44,7 +44,8 @@
"defaultTab": {
"description": "进入聊天对话后默认显示的页面",
"insights": "洞察",
"aiChat": "AI 对话"
"aiChat": "AI 对话",
"memory": "记忆"
},
"autoLaunch": {
"openAtLogin": "开机自启动",
+2 -1
View File
@@ -44,7 +44,8 @@
"defaultTab": {
"description": "進入聊天對話後預設顯示的頁面",
"insights": "洞察",
"aiChat": "AI 對話"
"aiChat": "AI 對話",
"memory": "記憶"
},
"autoLaunch": {
"openAtLogin": "開機自動啟動",
+2 -1
View File
@@ -85,7 +85,8 @@ const {
router,
currentSessionId,
selectSession: sessionStore.selectSession,
defaultTab: settingsStore.defaultSessionTab,
defaultTab:
IS_WEB_WASM && settingsStore.defaultSessionTab === 'memory' ? 'insights' : settingsStore.defaultSessionTab,
validTabIds: tabs.map((tab) => tab.id),
})
+2 -1
View File
@@ -80,7 +80,8 @@ const {
router,
currentSessionId,
selectSession: sessionStore.selectSession,
defaultTab: settingsStore.defaultSessionTab,
defaultTab:
IS_WEB_WASM && settingsStore.defaultSessionTab === 'memory' ? 'insights' : settingsStore.defaultSessionTab,
validTabIds: tabs.map((tab) => tab.id),
})
+1 -1
View File
@@ -34,7 +34,7 @@ export const useSettingsStore = defineStore(
() => {
const locale = ref<LocaleType>(getLocale())
const defaultSessionTab = ref<'insights' | 'ai-chat'>('insights')
const defaultSessionTab = ref<'insights' | 'ai-chat' | 'memory'>('insights')
const insightCardTheme = ref<InsightCardThemeId>(DEFAULT_INSIGHT_CARD_THEME)