From c63d9c9ff147daed469f53a400ab8b295682290f Mon Sep 17 00:00:00 2001 From: Bob Lee Date: Sun, 19 Jul 2026 09:09:03 +0800 Subject: [PATCH 1/2] feat(settings): move session title config into Models settings Keep auto title enablement and model selection with other model-related settings so personalization focuses on Agent companion. --- .../src/app/scenes/settings/settingsConfig.ts | 4 +- .../settings/settingsTabSearchContent.ts | 6 +- .../config/components/AIModelConfig.tsx | 3 + .../config/components/SessionConfig.tsx | 78 +--------- .../config/components/SessionTitleConfig.tsx | 147 ++++++++++++++++++ src/web-ui/src/locales/en-US/settings.json | 4 +- .../src/locales/en-US/settings/ai-model.json | 20 +++ .../en-US/settings/session-config.json | 16 +- src/web-ui/src/locales/zh-CN/settings.json | 4 +- .../src/locales/zh-CN/settings/ai-model.json | 20 +++ .../zh-CN/settings/session-config.json | 16 +- src/web-ui/src/locales/zh-TW/settings.json | 4 +- .../src/locales/zh-TW/settings/ai-model.json | 20 +++ .../zh-TW/settings/session-config.json | 16 +- 14 files changed, 226 insertions(+), 132 deletions(-) create mode 100644 src/web-ui/src/infrastructure/config/components/SessionTitleConfig.tsx diff --git a/src/web-ui/src/app/scenes/settings/settingsConfig.ts b/src/web-ui/src/app/scenes/settings/settingsConfig.ts index 8e6c347d7..f7377caf1 100644 --- a/src/web-ui/src/app/scenes/settings/settingsConfig.ts +++ b/src/web-ui/src/app/scenes/settings/settingsConfig.ts @@ -96,6 +96,9 @@ export const SETTINGS_CATEGORIES: ConfigCategoryDef[] = [ 'model', 'temperature', 'token', + 'session title', + 'auto title', + 'subagent', ], }, { @@ -135,7 +138,6 @@ export const SETTINGS_CATEGORIES: ConfigCategoryDef[] = [ descriptionKey: 'configCenter.tabDescriptions.sessionPersonalization', keywords: [ 'session', - 'title', 'companion', 'agent', 'pixel', diff --git a/src/web-ui/src/app/scenes/settings/settingsTabSearchContent.ts b/src/web-ui/src/app/scenes/settings/settingsTabSearchContent.ts index fdb77bda8..27885d52c 100644 --- a/src/web-ui/src/app/scenes/settings/settingsTabSearchContent.ts +++ b/src/web-ui/src/app/scenes/settings/settingsTabSearchContent.ts @@ -41,6 +41,9 @@ export const SETTINGS_TAB_SEARCH_CONTENT: Record { + + = ({ variant } const [companionPetImporting, setCompanionPetImporting] = useState(false); const [companionPetDeletingPath, setCompanionPetDeletingPath] = useState(null); const [companionPetListExpanded, setCompanionPetListExpanded] = useState(false); - const [models, setModels] = useState([]); - const [funcAgentModels, setFuncAgentModels] = useState>({}); const [skipToolConfirmation, setSkipToolConfirmation] = useState(true); const [enableDeferredToolLoading, setEnableDeferredToolLoading] = useState(true); const [subagentMaxConcurrency, setSubagentMaxConcurrency] = useState(DEFAULT_SUBAGENT_MAX_CONCURRENCY); @@ -209,8 +204,6 @@ const SessionSettingsPanels: React.FC = ({ variant } try { const [ loadedSettings, - allModels, - funcAgentModelsData, skipConfirm, deferredToolLoadingEnabled, loadedSubagentMaxConcurrency, @@ -223,8 +216,6 @@ const SessionSettingsPanels: React.FC = ({ variant } loadedCompanionPets, ] = await Promise.all([ aiExperienceConfigService.getSettingsAsync(), - configManager.getConfig('ai.models') || [], - configManager.getConfig>('ai.func_agent_models') || {}, configManager.getConfig('ai.skip_tool_confirmation'), configManager.getConfig('ai.enable_deferred_tool_loading'), configManager.getConfig('ai.subagent_max_concurrency'), @@ -239,8 +230,6 @@ const SessionSettingsPanels: React.FC = ({ variant } setSettings(loadedSettings); setCompanionPets(loadedCompanionPets); - setModels(allModels as AIModelConfig[]); - setFuncAgentModels(funcAgentModelsData as Record); setSkipToolConfirmation(skipConfirm ?? true); setEnableDeferredToolLoading(deferredToolLoadingEnabled ?? true); setSubagentMaxConcurrency(loadedSubagentMaxConcurrency != null @@ -431,37 +420,6 @@ const SessionSettingsPanels: React.FC = ({ variant } setCompanionPetListExpanded(false); }; - const getModelName = useCallback((modelId: string | null | undefined): string | undefined => { - if (!modelId) return undefined; - return models.find(m => m.id === modelId)?.name; - }, [models]); - - const handleAgentModelChange = async (agentKey: string, featureTitleKey: string, modelId: string) => { - try { - const current = await configManager.getConfig>('ai.func_agent_models') || {}; - const updated = { ...current, [agentKey]: modelId }; - await configManager.setConfig('ai.func_agent_models', updated); - setFuncAgentModels(updated); - - let modelDesc = ''; - if (modelId === 'primary') { - modelDesc = t('model.primary'); - } else if (modelId === 'fast') { - modelDesc = t('model.fast'); - } else { - modelDesc = getModelName(modelId) || modelId || ''; - } - - notificationService.success( - t('models.updateSuccess', { agentName: t(featureTitleKey), modelName: modelDesc }), - { duration: 2000 } - ); - } catch (error) { - log.error('Failed to update agent model', { agentKey, modelId, error }); - notificationService.error(t('messages.updateFailed'), { duration: 3000 }); - } - }; - const handleSkipToolConfirmationChange = async (checked: boolean) => { setSkipToolConfirmation(checked); setToolExecConfigLoading(true); @@ -812,8 +770,6 @@ const SessionSettingsPanels: React.FC = ({ variant } // ── Derived values ─────────────────────────────────────────────────────── - const enabledModels = models.filter((m: AIModelConfig) => m.enabled); - const sessionTitleModelId = funcAgentModels[AGENT_SESSION_TITLE] || 'fast'; const templateEntries = getTemplateEntries(); const computerUseAccessLabel = computerUseStatusLoading ? t('loading.text') @@ -857,38 +813,6 @@ const SessionSettingsPanels: React.FC = ({ variant } {variant === 'personalization' ? ( <> - {/* ── Auto session title ─────────────────────────────────── */} - - -
- updateSetting('enable_session_title_generation', e.target.checked)} - size="small" - /> -
-
- -
- handleAgentModelChange(AGENT_SESSION_TITLE, 'features.sessionTitle.title', modelId)} - layout="horizontal" - size="small" - /> -
-
-
- {/* ── Agent companion (collapsed input) ─────────────────── */} { + const { t } = useTranslation('settings/ai-model'); + const notification = useNotification(); + const [isLoading, setIsLoading] = useState(true); + const [settings, setSettings] = useState(null); + const [models, setModels] = useState([]); + const [funcAgentModels, setFuncAgentModels] = useState>({}); + + const loadData = useCallback(async () => { + setIsLoading(true); + try { + const [loadedSettings, allModels, funcAgentModelsData] = await Promise.all([ + aiExperienceConfigService.getSettingsAsync(), + configManager.getConfig('ai.models') || [], + configManager.getConfig>('ai.func_agent_models') || {}, + ]); + setSettings(loadedSettings); + setModels(allModels ?? []); + setFuncAgentModels(funcAgentModelsData ?? {}); + } catch (error) { + log.error('Failed to load session title config', error); + notification.error(t('sessionTitle.loadFailed')); + } finally { + setIsLoading(false); + } + }, [notification, t]); + + useEffect(() => { + void loadData(); + const unwatchModels = configManager.watch('ai.models', () => void loadData()); + const unwatchFuncAgentModels = configManager.watch('ai.func_agent_models', () => void loadData()); + const unwatchSettings = aiExperienceConfigService.addChangeListener((next) => { + setSettings(next); + }); + return () => { + unwatchModels(); + unwatchFuncAgentModels(); + unwatchSettings(); + }; + }, [loadData]); + + const enabledModels = models.filter((model) => model.enabled); + const sessionTitleModelId = funcAgentModels[AGENT_SESSION_TITLE] || 'fast'; + + const updateEnabled = async (checked: boolean) => { + if (!settings) return; + const previous = settings; + const next = { ...settings, enable_session_title_generation: checked }; + setSettings(next); + try { + await aiExperienceConfigService.saveSettings(next); + notification.success(t('sessionTitle.messages.saveSuccess')); + } catch (error) { + log.error('Failed to save session title enable setting', error); + notification.error(t('sessionTitle.messages.saveFailed')); + setSettings(previous); + } + }; + + const getModelName = useCallback((modelId: string | null | undefined): string | undefined => { + if (!modelId) return undefined; + return models.find((model) => model.id === modelId)?.name; + }, [models]); + + const handleModelChange = async (modelId: string) => { + try { + const current = await configManager.getConfig>('ai.func_agent_models') || {}; + const updated = { ...current, [AGENT_SESSION_TITLE]: modelId }; + await configManager.setConfig('ai.func_agent_models', updated); + setFuncAgentModels(updated); + + let modelDesc = ''; + if (modelId === 'primary') { + modelDesc = t('sessionTitle.model.primary'); + } else if (modelId === 'fast') { + modelDesc = t('sessionTitle.model.fast'); + } else { + modelDesc = getModelName(modelId) || modelId || ''; + } + + notificationService.success( + t('sessionTitle.models.updateSuccess', { + agentName: t('sessionTitle.title'), + modelName: modelDesc, + }), + { duration: 2000 }, + ); + } catch (error) { + log.error('Failed to update session title model', { modelId, error }); + notificationService.error(t('sessionTitle.messages.updateFailed'), { duration: 3000 }); + } + }; + + return ( + + +
+ void updateEnabled(e.target.checked)} + size="small" + disabled={isLoading || !settings} + /> +
+
+ +
+ void handleModelChange(modelId)} + layout="horizontal" + size="small" + disabled={isLoading} + /> +
+
+
+ ); +}; + +export default SessionTitleConfig; diff --git a/src/web-ui/src/locales/en-US/settings.json b/src/web-ui/src/locales/en-US/settings.json index f4a1f47e2..aee8e5d1b 100644 --- a/src/web-ui/src/locales/en-US/settings.json +++ b/src/web-ui/src/locales/en-US/settings.json @@ -17,8 +17,8 @@ "tabDescriptions": { "basics": "Logging, terminal shell, notifications, and launch at login.", "appearance": "Language, theme, and UI font size.", - "models": "AI models, API keys, providers, and proxy.", - "sessionPersonalization": "Session title and Agent companion.", + "models": "AI models, API keys, providers, proxy, and session title.", + "sessionPersonalization": "Agent companion.", "sessionPermissions": "Accelerated workspace search, tool confirmation, Computer use, browser, and debug.", "quickActions": "One-click AI actions after coding. Built-in and customizable prompts.", "review": "Review strategy, coverage depth, capacity, cost, and latency controls.", diff --git a/src/web-ui/src/locales/en-US/settings/ai-model.json b/src/web-ui/src/locales/en-US/settings/ai-model.json index e10d3b8a5..121e564e8 100644 --- a/src/web-ui/src/locales/en-US/settings/ai-model.json +++ b/src/web-ui/src/locales/en-US/settings/ai-model.json @@ -23,6 +23,26 @@ }, "loadFailed": "Failed to load Subagent model settings" }, + "sessionTitle": { + "title": "Auto Session Title", + "subtitle": "AI automatically generates concise titles for new conversations", + "enable": "Enable", + "loadFailed": "Failed to load session title settings", + "model": { + "label": "Model", + "primary": "Primary Model", + "fast": "Fast Model" + }, + "models": { + "empty": "No available models. Please add models in \"Models\" first.", + "updateSuccess": "{{agentName}} will use {{modelName}}" + }, + "messages": { + "saveSuccess": "Settings saved", + "saveFailed": "Save failed", + "updateFailed": "Update failed" + } + }, "cliAuth": { "sectionTitle": "Local CLI accounts", "sectionDescription": "Import Codex CLI / Gemini CLI accounts already signed in on this machine and use them as models", diff --git a/src/web-ui/src/locales/en-US/settings/session-config.json b/src/web-ui/src/locales/en-US/settings/session-config.json index 9acebcd90..5c7f045da 100644 --- a/src/web-ui/src/locales/en-US/settings/session-config.json +++ b/src/web-ui/src/locales/en-US/settings/session-config.json @@ -1,18 +1,13 @@ { "personalizationPage": { "title": "Personalization", - "subtitle": "Auto session title and Agent companion" + "subtitle": "Personalize your BitFun" }, "permissionsPage": { "title": "Permission management", "subtitle": "Accelerated workspace search, tool execution, tool definition loading, Computer use, browser control, and debug mode" }, "features": { - "sessionTitle": { - "title": "Auto Session Title", - "subtitle": "AI automatically generates concise titles for new conversations", - "warning": "Session title will use first 20 characters of user message" - }, "agentCompanion": { "title": "Agent companion", "subtitle": "Control where the BitFun companion appears.", @@ -101,18 +96,9 @@ "createLauncherDesc": "Create a browser shortcut with the debug port enabled.", "tabs": "tabs" }, - "model": { - "label": "Model", - "primary": "Primary Model", - "fast": "Fast Model" - }, "common": { "enable": "Enable" }, - "models": { - "empty": "No available models. Please add models in \"Models\" first.", - "updateSuccess": "{{agentName}} will use {{modelName}}" - }, "loading": { "text": "Loading configuration..." }, diff --git a/src/web-ui/src/locales/zh-CN/settings.json b/src/web-ui/src/locales/zh-CN/settings.json index e7a3ca25e..687978b8d 100644 --- a/src/web-ui/src/locales/zh-CN/settings.json +++ b/src/web-ui/src/locales/zh-CN/settings.json @@ -38,8 +38,8 @@ "tabDescriptions": { "basics": "日志、终端 Shell、通知与开机启动。", "appearance": "语言、主题与界面字体大小。", - "models": "AI 模型、API 密钥、供应商与代理。", - "sessionPersonalization": "会话标题与 Agent 伙伴。", + "models": "AI 模型、API 密钥、供应商、代理与会话标题。", + "sessionPersonalization": "Agent 伙伴。", "sessionPermissions": "加速工作区搜索、工具确认与超时、Computer use、浏览器与调试。", "quickActions": "代码完成后的一键 AI 动作,支持内置和自定义指令。", "review": "Review 策略、覆盖深度、容量、成本和耗时控制。", diff --git a/src/web-ui/src/locales/zh-CN/settings/ai-model.json b/src/web-ui/src/locales/zh-CN/settings/ai-model.json index 01c6bd2f9..6072df9c0 100644 --- a/src/web-ui/src/locales/zh-CN/settings/ai-model.json +++ b/src/web-ui/src/locales/zh-CN/settings/ai-model.json @@ -23,6 +23,26 @@ }, "loadFailed": "加载 Subagent 模型配置失败" }, + "sessionTitle": { + "title": "会话标题自动生成", + "subtitle": "新对话时 AI 自动生成简洁标题", + "enable": "启用", + "loadFailed": "加载会话标题配置失败", + "model": { + "label": "模型", + "primary": "主力模型", + "fast": "快速模型" + }, + "models": { + "empty": "暂无可用模型,请先在「模型」中添加模型。", + "updateSuccess": "{{agentName}} 将使用 {{modelName}}" + }, + "messages": { + "saveSuccess": "设置已保存", + "saveFailed": "保存失败", + "updateFailed": "设置失败" + } + }, "cliAuth": { "sectionTitle": "本机已登录账号", "sectionDescription": "可直接导入本机 Codex CLI / Gemini CLI 已登录账号作为模型使用", diff --git a/src/web-ui/src/locales/zh-CN/settings/session-config.json b/src/web-ui/src/locales/zh-CN/settings/session-config.json index 47eb6c70e..92945ca7f 100644 --- a/src/web-ui/src/locales/zh-CN/settings/session-config.json +++ b/src/web-ui/src/locales/zh-CN/settings/session-config.json @@ -1,18 +1,13 @@ { "personalizationPage": { "title": "个性化", - "subtitle": "会话标题自动生成、Agent 伙伴" + "subtitle": "个性化配置你的 BitFun" }, "permissionsPage": { "title": "权限管理", "subtitle": "加速工作区搜索、工具执行、工具定义加载、Computer use、浏览器控制与调试模式" }, "features": { - "sessionTitle": { - "title": "会话标题自动生成", - "subtitle": "新对话时 AI 自动生成简洁标题", - "warning": "会话标题将使用用户消息的前 20 个字符" - }, "agentCompanion": { "title": "Agent 伙伴", "subtitle": "控制 BitFun 伙伴的显示方式。", @@ -101,18 +96,9 @@ "createLauncherDesc": "创建带调试端口的浏览器快捷方式。", "tabs": "个标签页" }, - "model": { - "label": "模型", - "primary": "主力模型", - "fast": "快速模型" - }, "common": { "enable": "启用" }, - "models": { - "empty": "暂无可用模型,请先在「模型」中添加模型。", - "updateSuccess": "{{agentName}} 将使用 {{modelName}}" - }, "loading": { "text": "加载配置中..." }, diff --git a/src/web-ui/src/locales/zh-TW/settings.json b/src/web-ui/src/locales/zh-TW/settings.json index 02a522ca4..3f7d258bd 100644 --- a/src/web-ui/src/locales/zh-TW/settings.json +++ b/src/web-ui/src/locales/zh-TW/settings.json @@ -38,8 +38,8 @@ "tabDescriptions": { "basics": "日誌、終端 Shell、通知與開機啟動。", "appearance": "語言、主題與介面字體大小。", - "models": "AI 模型、API 密鑰、供應商與代理。", - "sessionPersonalization": "會話標題與 Agent 夥伴。", + "models": "AI 模型、API 密鑰、供應商、代理與會話標題。", + "sessionPersonalization": "Agent 夥伴。", "sessionPermissions": "加速工作區搜尋、工具確認與逾時、Computer use、瀏覽器與偵錯。", "review": "Review 策略、覆蓋深度、容量、成本和耗時控制。", "memories": "自動記憶生成、注入、整理窗口與記憶模型。", diff --git a/src/web-ui/src/locales/zh-TW/settings/ai-model.json b/src/web-ui/src/locales/zh-TW/settings/ai-model.json index ea5fbb702..5de565155 100644 --- a/src/web-ui/src/locales/zh-TW/settings/ai-model.json +++ b/src/web-ui/src/locales/zh-TW/settings/ai-model.json @@ -23,6 +23,26 @@ }, "loadFailed": "載入 Subagent 模型設定失敗" }, + "sessionTitle": { + "title": "會話標題自動生成", + "subtitle": "新對話時 AI 自動生成簡潔標題", + "enable": "啟用", + "loadFailed": "載入會話標題設定失敗", + "model": { + "label": "模型", + "primary": "主力模型", + "fast": "快速模型" + }, + "models": { + "empty": "暫無可用模型,請先在「模型」中新增模型。", + "updateSuccess": "{{agentName}} 將使用 {{modelName}}" + }, + "messages": { + "saveSuccess": "設定已儲存", + "saveFailed": "儲存失敗", + "updateFailed": "設定失敗" + } + }, "cliAuth": { "sectionTitle": "本機已登錄賬號", "sectionDescription": "可直接匯入本機 Codex CLI / Gemini CLI 已登錄賬號作為模型使用", diff --git a/src/web-ui/src/locales/zh-TW/settings/session-config.json b/src/web-ui/src/locales/zh-TW/settings/session-config.json index 80ae03822..37a43bb99 100644 --- a/src/web-ui/src/locales/zh-TW/settings/session-config.json +++ b/src/web-ui/src/locales/zh-TW/settings/session-config.json @@ -1,18 +1,13 @@ { "personalizationPage": { "title": "個性化", - "subtitle": "會話標題自動生成、Agent 夥伴" + "subtitle": "個性化配置你的 BitFun" }, "permissionsPage": { "title": "權限管理", "subtitle": "加速工作區搜尋、工具執行、工具定義載入、Computer use、瀏覽器控制與偵錯模式" }, "features": { - "sessionTitle": { - "title": "會話標題自動生成", - "subtitle": "新對話時 AI 自動生成簡潔標題", - "warning": "會話標題將使用用戶消息的前 20 個字符" - }, "agentCompanion": { "title": "Agent 夥伴", "subtitle": "控制 BitFun 夥伴的顯示方式。", @@ -101,18 +96,9 @@ "preferredBrowserDesc": "選擇要由 BitFun 透過 CDP 控制的瀏覽器;預設表示跟隨系統預設瀏覽器。", "notInstalled": "未安裝" }, - "model": { - "label": "模型", - "primary": "主力模型", - "fast": "快速模型" - }, "common": { "enable": "啟用" }, - "models": { - "empty": "暫無可用模型,請先在「模型」中新增模型。", - "updateSuccess": "{{agentName}} 將使用 {{modelName}}" - }, "loading": { "text": "載入設定中..." }, From 1bb113a8553da6df93206abbb69c2a534b6d76f3 Mon Sep 17 00:00:00 2001 From: Bob Lee Date: Sun, 19 Jul 2026 09:10:40 +0800 Subject: [PATCH 2/2] fix(desktop): use a separate dev identifier for single-instance lock Point tauri.dev.conf.json at com.bitfun.desktop.dev so desktop:dev and Release can run side by side without sharing the same instance lock. --- src/apps/desktop/tauri.dev.conf.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/apps/desktop/tauri.dev.conf.json b/src/apps/desktop/tauri.dev.conf.json index b0f92544d..a9ee61ebc 100644 --- a/src/apps/desktop/tauri.dev.conf.json +++ b/src/apps/desktop/tauri.dev.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "BitFun", - "identifier": "com.bitfun.desktop", + "identifier": "com.bitfun.desktop.dev", "build": { "beforeDevCommand": "pnpm run dev:web", "devUrl": "http://localhost:1422",