From ead0576dd83aa2607f9f856190ff5d0f6522be00 Mon Sep 17 00:00:00 2001 From: wsp Date: Mon, 9 Feb 2026 18:31:14 +0800 Subject: [PATCH 1/5] chore: use pnpm tauri CLI to avoid cargo install Update desktop scripts/config and docs to rely on pnpm tauri. --- CONTRIBUTING.md | 8 ++++---- CONTRIBUTING_CN.md | 4 ++-- README.md | 25 +++++++++++++++++++++++++ README.zh-CN.md | 27 ++++++++++++++++++++++++++- package.json | 12 ++++++------ pnpm-lock.yaml | 2 +- scripts/dev.cjs | 2 +- src/apps/desktop/tauri.conf.json | 4 ++-- 8 files changed, 67 insertions(+), 17 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2983a90a..ef0986ab0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,14 +13,14 @@ Be respectful, kind, and constructive. We welcome contributors of all background ### Prerequisites - Node.js (LTS recommended) -- npm -- Rust toolchain (install via rustup) -- Tauri dependencies for desktop development +- pnpm (run `corepack enable`) +- Rust toolchain (install via [rustup](https://rustup.rs/)) +- [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) for desktop development ### Install dependencies ```bash -npm install +pnpm install ``` ### Common commands diff --git a/CONTRIBUTING_CN.md b/CONTRIBUTING_CN.md index f887b8931..755ba1d3d 100644 --- a/CONTRIBUTING_CN.md +++ b/CONTRIBUTING_CN.md @@ -13,14 +13,14 @@ ### 环境准备 - Node.js(建议 LTS 版本) -- npm +- pnpm(执行 `corepack enable`) - Rust toolchain(通过 rustup 安装) - 桌面端开发需准备 Tauri 依赖 ### 安装依赖 ```bash -npm install +pnpm install ``` ### 常用命令 diff --git a/README.md b/README.md index cd4e9fbc9..1ed7832e6 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,35 @@ BitFun is an Agentic Development Environment (ADE). While featuring a cutting-ed ## Quick Start + +### Use Directly + Download the latest installer for the desktop app from [Release](https://github.com/GCWing/BitFun/releases). After installation, configure your model and you're ready to go. Other form factors are currently only specification drafts and not yet developed. If needed, please build from source. +### Build from Source + +Make sure you have the following prerequisites installed: + +- Node.js (LTS recommended) +- pnpm (run `corepack enable`) +- Rust toolchain (install via [rustup](https://rustup.rs/)) +- [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/) for desktop development + +```bash +# Install dependencies +pnpm install + +# Run desktop app in development mode +npm run desktop:dev + +# Build desktop app +npm run desktop:build +``` + +For more details, see the [Contributing Guide](./CONTRIBUTING.md). + ## Platform Support The project uses a Rust + TypeScript tech stack, supporting cross-platform and multi-form-factor reuse. diff --git a/README.zh-CN.md b/README.zh-CN.md index 5517770dc..f24e37f3d 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -48,10 +48,35 @@ BitFun 是一款代理式开发环境(ADE,Agentic Development Environment) ## 快速开始 -桌面端程序在[Release](https://github.com/GCWing/BitFun/releases)处下载最新安装包,安装后配置模型即可开始使用。 + +### 直接使用 + +桌面端程序在 [Release](https://github.com/GCWing/BitFun/releases) 处下载最新安装包,安装后配置模型即可开始使用。 其他形态暂时仅是规范雏形未完成开发,如有需要请从源码构建。 +### 从源码构建 + +请确保已安装以下前置依赖: + +- Node.js(推荐 LTS 版本) +- pnpm(执行 `corepack enable`) +- Rust 工具链(通过 [rustup](https://rustup.rs/) 安装) +- [Tauri 前置依赖](https://v2.tauri.app/start/prerequisites/)(桌面端开发需要) + +```bash +# 安装依赖 +pnpm install + +# 以开发模式运行桌面端 +npm run desktop:dev + +# 构建桌面端 +npm run desktop:build +``` + +更多详情请参阅[贡献指南](./CONTRIBUTING_CN.md)。 + ## 平台支持 项目采用 Rust + TypeScript 技术栈,支持跨平台和多形态复用。 diff --git a/package.json b/package.json index f3f6b8555..fd0b2f3a8 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,11 @@ "build:web": "cd src/web-ui && vite build", "preview": "cd src/web-ui && vite preview", "desktop:dev": "node scripts/dev.cjs desktop", - "desktop:dev:raw": "cd src/apps/desktop && cargo tauri dev", - "desktop:build": "npm run build:web && cd src/apps/desktop && cargo tauri build", - "desktop:build:exe": "npm run build:web && cd src/apps/desktop && cargo tauri build --no-bundle", - "desktop:build:nsis": "npm run build:web && cd src/apps/desktop && cargo tauri build --bundles nsis", - "desktop:build:x86_64": "npm run build:web && cd src/apps/desktop && cargo tauri build --target x86_64-apple-darwin", + "desktop:dev:raw": "cd src/apps/desktop && pnpm tauri dev", + "desktop:build": "npm run build:web && cd src/apps/desktop && pnpm tauri build", + "desktop:build:exe": "npm run build:web && cd src/apps/desktop && pnpm tauri build --no-bundle", + "desktop:build:nsis": "npm run build:web && cd src/apps/desktop && pnpm tauri build --bundles nsis", + "desktop:build:x86_64": "npm run build:web && cd src/apps/desktop && pnpm tauri build --target x86_64-apple-darwin", "cli:dev": "cd src/apps/cli && cargo run --", "cli:build": "cd src/apps/cli && cargo build --release", "cli:run": "cd src/apps/cli && cargo run --release --", @@ -52,7 +52,7 @@ "@lezer/highlight": "^1.2.1", "@monaco-editor/react": "^4.6.0", "@react-sigma/core": "^5.0.6", - "@tauri-apps/api": "~2.9.0", + "@tauri-apps/api": "^2", "@tauri-apps/plugin-dialog": "^2.6", "@tauri-apps/plugin-fs": "^2", "@tauri-apps/plugin-log": "^2.8.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92e2d507a..183a91c3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,7 +36,7 @@ importers: specifier: ^5.0.6 version: 5.0.6(graphology@0.26.0(graphology-types@0.24.8))(react@18.3.1)(sigma@3.0.2(graphology-types@0.24.8)) '@tauri-apps/api': - specifier: ^2.10.1 + specifier: ^2 version: 2.10.1 '@tauri-apps/plugin-dialog': specifier: ^2.6 diff --git a/scripts/dev.cjs b/scripts/dev.cjs index 84630dc62..212510b97 100644 --- a/scripts/dev.cjs +++ b/scripts/dev.cjs @@ -110,7 +110,7 @@ async function main() { try { if (mode === 'desktop') { - await runCommand('cargo tauri dev', path.join(ROOT_DIR, 'src/apps/desktop')); + await runCommand('pnpm tauri dev', path.join(ROOT_DIR, 'src/apps/desktop')); } else { await runCommand('npx vite', path.join(ROOT_DIR, 'src/web-ui')); } diff --git a/src/apps/desktop/tauri.conf.json b/src/apps/desktop/tauri.conf.json index f66faa128..0e2d07705 100644 --- a/src/apps/desktop/tauri.conf.json +++ b/src/apps/desktop/tauri.conf.json @@ -4,9 +4,9 @@ "version": "0.1.0", "identifier": "com.bitfun.desktop", "build": { - "beforeDevCommand": "cd ../.. && npm run dev:web", + "beforeDevCommand": "npm run dev:web", "devUrl": "http://localhost:1422", - "beforeBuildCommand": "cd ../.. && npm run build:web", + "beforeBuildCommand": "npm run build:web", "frontendDist": "../../../dist" }, "bundle": { From e8be064f84f68e2cedd86d8adab1588a1239a1b7 Mon Sep 17 00:00:00 2001 From: wsp Date: Mon, 9 Feb 2026 18:38:47 +0800 Subject: [PATCH 2/5] fix: add Git tool to agentic mode default tools --- src/crates/core/src/agentic/agents/agentic_mode.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/crates/core/src/agentic/agents/agentic_mode.rs b/src/crates/core/src/agentic/agents/agentic_mode.rs index 9dad2cde4..1d9ff5347 100644 --- a/src/crates/core/src/agentic/agents/agentic_mode.rs +++ b/src/crates/core/src/agentic/agents/agentic_mode.rs @@ -26,6 +26,7 @@ impl AgenticMode { "AnalyzeImage".to_string(), "Skill".to_string(), "AskUserQuestion".to_string(), + "Git".to_string(), ], } } From c1157fab60fce32ec429c6dfcb6402901a3b21de Mon Sep 17 00:00:00 2001 From: wsp Date: Mon, 9 Feb 2026 18:44:22 +0800 Subject: [PATCH 3/5] chore: improve error output for setup steps --- scripts/dev.cjs | 66 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/scripts/dev.cjs b/scripts/dev.cjs index 212510b97..1949099fd 100644 --- a/scripts/dev.cjs +++ b/scripts/dev.cjs @@ -24,26 +24,58 @@ const ROOT_DIR = path.resolve(__dirname, '..'); */ function runSilent(command, cwd = ROOT_DIR) { try { - execSync(command, { + const stdout = execSync(command, { cwd, stdio: 'pipe', - encoding: 'utf-8' + encoding: 'buffer' }); - return true; + return { ok: true, stdout: decodeOutput(stdout), stderr: '' }; } catch (error) { - return false; + const stdout = error.stdout ? decodeOutput(error.stdout) : ''; + const stderr = error.stderr ? decodeOutput(error.stderr) : ''; + return { ok: false, stdout, stderr, error }; } } +function decodeOutput(output) { + if (!output) return ''; + if (typeof output === 'string') return output; + const buffer = Buffer.isBuffer(output) ? output : Buffer.from(output); + if (process.platform !== 'win32') return buffer.toString('utf-8'); + + const utf8 = buffer.toString('utf-8'); + if (!utf8.includes('�')) return utf8; + + try { + const { TextDecoder } = require('util'); + const decoder = new TextDecoder('gbk'); + const gbk = decoder.decode(buffer); + if (gbk && !gbk.includes('�')) return gbk; + return gbk || utf8; + } catch (error) { + return utf8; + } +} + +function tailOutput(output, maxLines = 12) { + if (!output) return ''; + const lines = output + .split(/\r?\n/) + .map((line) => line.trimEnd()) + .filter((line) => line.trim() !== ''); + if (lines.length <= maxLines) return lines.join('\n'); + return lines.slice(-maxLines).join('\n'); +} + /** * Run command with inherited output */ function runInherit(command, cwd = ROOT_DIR) { try { execSync(command, { cwd, stdio: 'inherit' }); - return true; + return { ok: true, error: null }; } catch (error) { - return false; + return { ok: false, error }; } } @@ -86,17 +118,35 @@ async function main() { // Step 1: Copy resources printStep(1, 3, 'Copy resources'); - if (runSilent('npm run copy-monaco --silent')) { + const copyResult = runSilent('npm run copy-monaco --silent'); + if (copyResult.ok) { printSuccess('Monaco Editor resources ready'); } else { printError('Copy resources failed'); + const output = tailOutput(copyResult.stderr || copyResult.stdout); + if (output) { + printError(output); + } else if (copyResult.error) { + printError(copyResult.error.message); + } + if (copyResult.error && copyResult.error.status !== undefined) { + printError(`Exit code: ${copyResult.error.status}`); + } + printInfo('Hint: run `pnpm install` in repo root if dependencies are missing'); process.exit(1); } // Step 2: Generate version info printStep(2, 3, 'Generate version info'); - if (!runInherit('node scripts/generate-version.cjs')) { + const versionResult = runInherit('node scripts/generate-version.cjs'); + if (!versionResult.ok) { printError('Generate version info failed'); + if (versionResult.error && versionResult.error.message) { + printError(versionResult.error.message); + } + if (versionResult.error && versionResult.error.status !== undefined) { + printError(`Exit code: ${versionResult.error.status}`); + } process.exit(1); } From d4f59d1ec469fa1846a4e79392a7809764777251 Mon Sep 17 00:00:00 2001 From: wsp Date: Mon, 9 Feb 2026 18:52:09 +0800 Subject: [PATCH 4/5] feat: add visual mode toggle for Agentic mode - Add enable_visual_mode field to AIExperienceConfig (Rust & TS) - Add VISUAL_MODE placeholder in PromptBuilder to inject Mermaid diagram instructions when visual mode is enabled - Implement visual mode switch in AgenticModeConfig panel with immediate persistence via configAPI - Remove unused priority strategy UI - Change tool toggles to save immediately instead of requiring manual save button click - Fix GlobalConfigManager::reload Arc divergence causing config writes via AppState to desync from reads via GlobalConfigManager --- .../agents/prompt_builder/prompt_builder.rs | 34 +++++ .../agentic/agents/prompts/agentic_mode.md | 1 + src/crates/core/src/service/config/global.rs | 11 +- src/crates/core/src/service/config/manager.rs | 4 +- src/crates/core/src/service/config/types.rs | 5 +- .../config/components/AIFeaturesConfig.tsx | 12 +- .../config/components/AgenticModeConfig.tsx | 132 +++++++----------- .../services/AIExperienceConfigService.ts | 12 +- .../src/infrastructure/config/types/index.ts | 7 +- 9 files changed, 114 insertions(+), 104 deletions(-) diff --git a/src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs b/src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs index 6913d6be5..bd4885988 100644 --- a/src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs +++ b/src/crates/core/src/agentic/agents/prompt_builder/prompt_builder.rs @@ -17,6 +17,7 @@ const PLACEHOLDER_PROJECT_LAYOUT: &str = "{PROJECT_LAYOUT}"; const PLACEHOLDER_RULES: &str = "{RULES}"; const PLACEHOLDER_MEMORIES: &str = "{MEMORIES}"; const PLACEHOLDER_LANGUAGE_PREFERENCE: &str = "{LANGUAGE_PREFERENCE}"; +const PLACEHOLDER_VISUAL_MODE: &str = "{VISUAL_MODE}"; pub struct PromptBuilder { pub workspace_path: String, @@ -156,6 +157,32 @@ These files are maintained by the user and should NOT be modified unless explici } } + /// Get visual mode instruction from user config + /// + /// Reads `app.ai_experience.enable_visual_mode` from global config. + /// Returns a prompt snippet when enabled, or empty string when disabled. + async fn get_visual_mode_instruction(&self) -> String { + let enabled = match GlobalConfigManager::get_service().await { + Ok(service) => service + .get_config::(Some("app.ai_experience.enable_visual_mode")) + .await + .unwrap_or(false), + Err(e) => { + debug!("Failed to read visual mode config: {}", e); + false + } + }; + + if enabled { + r"# Visualizing complex logic as you explain +Use Mermaid diagrams to visualize complex logic, workflows, architectures, and data flows whenever it helps clarify the explanation. +Prefer MermaidInteractive tool when available, otherwise output Mermaid code blocks directly. +".to_string() + } else { + String::new() + } + } + /// Get user language preference instruction /// /// Read app.language from global config, generate simple language instruction @@ -194,6 +221,7 @@ These files are maintained by the user and should NOT be modified unless explici /// - `{PROJECT_CONTEXT_FILES}` - Project context files (AGENTS.md, CLAUDE.md, etc.) /// - `{RULES}` - AI rules /// - `{MEMORIES}` - AI memories + /// - `{VISUAL_MODE}` - Visual mode instruction (Mermaid diagrams, read from global config) /// /// If a placeholder is not in the template, corresponding content will not be added pub async fn build_prompt_from_template(&self, template: &str) -> BitFunResult { @@ -264,6 +292,12 @@ These files are maintained by the user and should NOT be modified unless explici result = result.replace(PLACEHOLDER_MEMORIES, &memories); } + // Replace {VISUAL_MODE} + if result.contains(PLACEHOLDER_VISUAL_MODE) { + let visual_mode = self.get_visual_mode_instruction().await; + result = result.replace(PLACEHOLDER_VISUAL_MODE, &visual_mode); + } + Ok(result.trim().to_string()) } } diff --git a/src/crates/core/src/agentic/agents/prompts/agentic_mode.md b/src/crates/core/src/agentic/agents/prompts/agentic_mode.md index 29c90f07e..4c4bb0172 100644 --- a/src/crates/core/src/agentic/agents/prompts/agentic_mode.md +++ b/src/crates/core/src/agentic/agents/prompts/agentic_mode.md @@ -71,6 +71,7 @@ I've found some existing telemetry code. Let me mark the first todo as in_progre # Asking questions as you work You have access to the AskUserQuestion tool to ask the user questions when you need clarification, want to validate assumptions, or need to make a decision you're unsure about. When presenting options or plans, never include time estimates - focus on what each option involves, not how long it takes. +{VISUAL_MODE} # Doing tasks The user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended: - NEVER propose changes to code you haven't read. If a user asks about or wants you to modify a file, read it first. Understand existing code before suggesting modifications. diff --git a/src/crates/core/src/service/config/global.rs b/src/crates/core/src/service/config/global.rs index 1c9a1f02a..585d5f466 100644 --- a/src/crates/core/src/service/config/global.rs +++ b/src/crates/core/src/service/config/global.rs @@ -124,10 +124,15 @@ impl GlobalConfigManager { Ok(()) } - /// Reloads configuration. + /// Reloads configuration in-place. + /// + /// Re-reads the config from disk into the existing `ConfigService` instance, + /// preserving the `Arc` pointer so that all holders (e.g. `AppState`) stay in sync. pub async fn reload() -> BitFunResult<()> { - let new_service = Arc::new(ConfigService::new().await?); - Self::update_service(new_service).await + let service = Self::get_service().await?; + service.reload().await?; + Self::broadcast_update(ConfigUpdateEvent::ConfigReloaded).await; + Ok(()) } /// Subscribes to configuration update events. diff --git a/src/crates/core/src/service/config/manager.rs b/src/crates/core/src/service/config/manager.rs index 418b3b4ac..77d9f48ca 100644 --- a/src/crates/core/src/service/config/manager.rs +++ b/src/crates/core/src/service/config/manager.rs @@ -602,8 +602,8 @@ pub(crate) fn migrate_0_0_0_to_1_0_0(mut config: Value) -> BitFunResult { app.insert( "ai_experience".to_string(), serde_json::json!({ - "enableSessionTitleGeneration": true, - "enableWelcomePanelAiAnalysis": true + "enable_session_title_generation": true, + "enable_welcome_panel_ai_analysis": true }), ); } diff --git a/src/crates/core/src/service/config/types.rs b/src/crates/core/src/service/config/types.rs index 839210f50..30294b07a 100644 --- a/src/crates/core/src/service/config/types.rs +++ b/src/crates/core/src/service/config/types.rs @@ -48,12 +48,14 @@ pub struct AppConfig { /// AI experience configuration. #[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(default, rename_all = "camelCase")] +#[serde(default)] pub struct AIExperienceConfig { /// Whether to enable automatic AI-generated summaries for session titles. pub enable_session_title_generation: bool, /// Whether to enable AI analysis of work status on the FlowChat welcome page. pub enable_welcome_panel_ai_analysis: bool, + /// Whether to enable visual mode. + pub enable_visual_mode: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -862,6 +864,7 @@ impl Default for AIExperienceConfig { Self { enable_session_title_generation: true, enable_welcome_panel_ai_analysis: true, + enable_visual_mode: false, } } } diff --git a/src/web-ui/src/infrastructure/config/components/AIFeaturesConfig.tsx b/src/web-ui/src/infrastructure/config/components/AIFeaturesConfig.tsx index a09f4b10d..ed2b15525 100644 --- a/src/web-ui/src/infrastructure/config/components/AIFeaturesConfig.tsx +++ b/src/web-ui/src/infrastructure/config/components/AIFeaturesConfig.tsx @@ -19,13 +19,13 @@ const log = createLogger('AIFeaturesConfig'); interface AIExperienceSettings { - enableSessionTitleGeneration: boolean; - enableWelcomePanelAiAnalysis: boolean; + enable_session_title_generation: boolean; + enable_welcome_panel_ai_analysis: boolean; } const defaultSettings: AIExperienceSettings = { - enableSessionTitleGeneration: true, - enableWelcomePanelAiAnalysis: true, + enable_session_title_generation: true, + enable_welcome_panel_ai_analysis: true, }; @@ -39,12 +39,12 @@ interface FeatureConfig { const FEATURE_CONFIGS: FeatureConfig[] = [ { id: 'sessionTitle', - settingKey: 'enableSessionTitleGeneration', + settingKey: 'enable_session_title_generation', agentName: 'startchat-func-agent', }, { id: 'welcomeAnalysis', - settingKey: 'enableWelcomePanelAiAnalysis', + settingKey: 'enable_welcome_panel_ai_analysis', agentName: 'startchat-func-agent', }, { diff --git a/src/web-ui/src/infrastructure/config/components/AgenticModeConfig.tsx b/src/web-ui/src/infrastructure/config/components/AgenticModeConfig.tsx index 0089bcde8..46038c8ce 100644 --- a/src/web-ui/src/infrastructure/config/components/AgenticModeConfig.tsx +++ b/src/web-ui/src/infrastructure/config/components/AgenticModeConfig.tsx @@ -1,21 +1,16 @@ import React, { useState, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import { RotateCcw, ListChecks, X, Save } from 'lucide-react'; -import { IconButton, Card, Switch, Select } from '@/component-library'; +import { RotateCcw, ListChecks, X } from 'lucide-react'; +import { IconButton, Card, Switch } from '@/component-library'; import { notificationService } from '@/shared/notification-system'; import { configAPI } from '@/infrastructure/api'; -import type { ModeConfigItem } from '../types'; +import type { ModeConfigItem, AIExperienceConfig } from '../types'; import { ConfigPageHeader, ConfigPageLayout, ConfigPageContent } from './common'; import { createLogger } from '@/shared/utils/logger'; import './AgenticModeConfig.scss'; const log = createLogger('AgenticModeConfig'); -interface AgenticPreferences { - visualMode: boolean; - priorityStrategy: 'economy' | 'quality' | 'balanced'; -} - interface ToolInfo { name: string; description: string; @@ -31,11 +26,7 @@ export const AgenticModeConfig: React.FC = ({ embedded = const [loading, setLoading] = useState(false); const [availableTools, setAvailableTools] = useState([]); const [agenticConfig, setAgenticConfig] = useState(null); - - const [preferences, setPreferences] = useState({ - visualMode: false, - priorityStrategy: 'balanced', - }); + const [visualMode, setVisualMode] = useState(false); const [toolsCollapsed, setToolsCollapsed] = useState(true); @@ -47,15 +38,19 @@ export const AgenticModeConfig: React.FC = ({ embedded = try { setLoading(true); - const [toolsData, modeConfig] = await Promise.all([ + const [toolsData, modeConfig, aiExperience] = await Promise.all([ fetchAvailableTools(), - configAPI.getModeConfig('agentic') + configAPI.getModeConfig('agentic'), + configAPI.getConfig('app.ai_experience') as Promise ]); setAvailableTools(toolsData); setAgenticConfig(modeConfig); + if (aiExperience) { + setVisualMode(aiExperience.enable_visual_mode ?? false); + } - log.debug('Data loaded', { toolsCount: toolsData.length, agenticConfig: modeConfig }); + log.debug('Data loaded', { toolsCount: toolsData.length, agenticConfig: modeConfig, visualMode: aiExperience?.enable_visual_mode }); } catch (error) { log.error('Failed to load data', { error }); const errorMsg = error instanceof Error ? error.message : 'Unknown error'; @@ -78,6 +73,21 @@ export const AgenticModeConfig: React.FC = ({ embedded = } }; + const saveToolsConfig = async (newConfig: ModeConfigItem) => { + try { + setAgenticConfig(newConfig); + await configAPI.setModeConfig('agentic', newConfig); + + const { globalEventBus } = await import('@/infrastructure/event-bus'); + globalEventBus.emit('mode:config:updated'); + log.debug('Mode config saved and event emitted'); + } catch (error) { + log.error('Failed to save tools config', { error }); + setAgenticConfig(agenticConfig); + notificationService.error(`${t('messages.saveFailed')}: ` + (error instanceof Error ? error.message : String(error))); + } + }; + const toggleTool = async (toolName: string) => { if (!agenticConfig) return; @@ -88,45 +98,17 @@ export const AgenticModeConfig: React.FC = ({ embedded = ? [...tools, toolName] : tools.filter(t => t !== toolName); - setAgenticConfig({ - ...agenticConfig, - available_tools: newTools - }); + await saveToolsConfig({ ...agenticConfig, available_tools: newTools }); }; - const selectAllTools = () => { + const selectAllTools = async () => { if (!agenticConfig) return; - setAgenticConfig({ - ...agenticConfig, - available_tools: availableTools.map(t => t.name) - }); + await saveToolsConfig({ ...agenticConfig, available_tools: availableTools.map(t => t.name) }); }; - const clearAllTools = () => { + const clearAllTools = async () => { if (!agenticConfig) return; - setAgenticConfig({ - ...agenticConfig, - available_tools: [] - }); - }; - - const saveToolsConfig = async () => { - if (!agenticConfig) return; - - try { - setLoading(true); - await configAPI.setModeConfig('agentic', agenticConfig); - notificationService.success(t('messages.saveSuccess')); - - const { globalEventBus } = await import('@/infrastructure/event-bus'); - globalEventBus.emit('mode:config:updated'); - log.debug('Mode config update event emitted'); - } catch (error) { - log.error('Failed to save tools config', { error }); - notificationService.error(`${t('messages.saveFailed')}: ` + (error instanceof Error ? error.message : String(error))); - } finally { - setLoading(false); - } + await saveToolsConfig({ ...agenticConfig, available_tools: [] }); }; const resetToolsConfig = async () => { @@ -147,6 +129,19 @@ export const AgenticModeConfig: React.FC = ({ embedded = } }; + const handleVisualModeChange = async (e: React.ChangeEvent) => { + const checked = e.target.checked; + setVisualMode(checked); + try { + await configAPI.setConfig('app.ai_experience.enable_visual_mode', checked); + log.debug('Visual mode updated', { enabled: checked }); + } catch (error) { + log.error('Failed to save visual mode config', { error }); + setVisualMode(!checked); + notificationService.error(t('messages.saveFailed', 'Failed to save')); + } + }; + return ( = ({ embedded = {t('preferences.visualMode.label', 'Visual Mode')} - {t('preferences.visualMode.description', 'Enable visual feedback during execution')} + {t('preferences.visualMode.description', 'Use Mermaid diagrams to visualize complex logic and flows')} setPreferences(prev => ({ ...prev, visualMode: checked }))} + checked={visualMode} + onChange={handleVisualModeChange} size="small" + disabled={loading} /> - -
-
- - {t('preferences.priorityStrategy.label', 'Priority Strategy')} - - - {t('preferences.priorityStrategy.description', 'Control model selection and invocation strategy')} - -
-
-