From a740a42761d8bd7081b8e8cb25c11734bd144717 Mon Sep 17 00:00:00 2001 From: lee Date: Sun, 26 Apr 2026 14:37:16 +0800 Subject: [PATCH 1/4] feat(settings): add configurable file open targets [T-04-25-open-file-in-editor] Add General settings for default open targets and route file preview/tree actions through the selected target. --- src-tauri/src/commands/system_settings.rs | 279 +++++++++++++++++- src-tauri/src/commands/windows.rs | 3 +- src-tauri/src/lib.rs | 13 +- src-tauri/src/models/mod.rs | 8 +- src-tauri/src/models/system.rs | 24 ++ src-tauri/src/web/handlers/folders.rs | 3 +- src-tauri/src/web/handlers/system_settings.rs | 30 ++ src-tauri/src/web/router.rs | 12 + src/app/settings/general/page.tsx | 5 + src/app/settings/page.tsx | 2 +- .../files/file-workspace-tab-bar.tsx | 192 +++++++++++- .../layout/aux-panel-file-tree-tab.tsx | 246 +++++++++------ src/components/settings/general-settings.tsx | 209 +++++++++++++ src/components/settings/settings-shell.tsx | 7 + src/i18n/messages/ar.json | 43 ++- src/i18n/messages/de.json | 43 ++- src/i18n/messages/en.json | 43 ++- src/i18n/messages/es.json | 43 ++- src/i18n/messages/fr.json | 43 ++- src/i18n/messages/ja.json | 43 ++- src/i18n/messages/ko.json | 43 ++- src/i18n/messages/pt.json | 43 ++- src/i18n/messages/zh-CN.json | 43 ++- src/i18n/messages/zh-TW.json | 43 ++- src/lib/api.ts | 25 ++ src/lib/open-targets.ts | 114 +++++++ src/lib/platform.ts | 7 +- src/lib/tauri.ts | 1 + src/lib/transport/index.ts | 2 +- src/lib/types.ts | 9 + 30 files changed, 1476 insertions(+), 145 deletions(-) create mode 100644 src/app/settings/general/page.tsx create mode 100644 src/components/settings/general-settings.tsx create mode 100644 src/lib/open-targets.ts diff --git a/src-tauri/src/commands/system_settings.rs b/src-tauri/src/commands/system_settings.rs index 49530620..a6058447 100644 --- a/src-tauri/src/commands/system_settings.rs +++ b/src-tauri/src/commands/system_settings.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "tauri-runtime")] +use std::path::{Component, Path, PathBuf}; + use sea_orm::DatabaseConnection; #[cfg(feature = "tauri-runtime")] use tauri::State; @@ -6,16 +9,17 @@ use crate::app_error::AppCommandError; use crate::db::service::app_metadata_service; #[cfg(feature = "tauri-runtime")] use crate::db::AppDatabase; -use crate::models::{SystemLanguageSettings, SystemProxySettings}; -#[cfg(feature = "tauri-runtime")] -use crate::models::SystemRenderingSettings; #[cfg(feature = "tauri-runtime")] -use crate::preferences; +use crate::models::{SystemOpenTarget, SystemRenderingSettings}; +use crate::models::{SystemLanguageSettings, SystemOpenTargetSettings, SystemProxySettings}; #[cfg(feature = "tauri-runtime")] use crate::network::proxy; +#[cfg(feature = "tauri-runtime")] +use crate::preferences; const SYSTEM_PROXY_SETTINGS_KEY: &str = "system_proxy_settings"; const SYSTEM_LANGUAGE_SETTINGS_KEY: &str = "system_language_settings"; +const SYSTEM_OPEN_TARGET_SETTINGS_KEY: &str = "system_open_target_settings"; #[cfg(feature = "tauri-runtime")] const LANGUAGE_SETTINGS_UPDATED_EVENT: &str = "app://language-settings-updated"; @@ -55,6 +59,179 @@ fn normalize_proxy_settings( }) } +fn normalize_open_target_settings(settings: SystemOpenTargetSettings) -> SystemOpenTargetSettings { + settings +} + +#[cfg(feature = "tauri-runtime")] +struct ResolvedWorkspacePath { + root: PathBuf, + target: PathBuf, +} + +#[cfg(feature = "tauri-runtime")] +fn resolve_workspace_relative_path( + folder_path: &str, + relative_path: &str, +) -> Result { + let root = PathBuf::from(folder_path); + if !root.exists() || !root.is_dir() { + return Err(AppCommandError::not_found("Folder does not exist")); + } + + let rel = Path::new(relative_path); + if rel.is_absolute() { + return Err(AppCommandError::invalid_input("Path must be relative")); + } + + for component in rel.components() { + match component { + Component::Normal(_) | Component::CurDir => {} + Component::ParentDir => { + return Err(AppCommandError::invalid_input("Path cannot contain '..'")); + } + Component::RootDir | Component::Prefix(_) => { + return Err(AppCommandError::invalid_input("Invalid path component")); + } + } + } + + let target = root.join(rel); + if !target.exists() { + return Err(AppCommandError::not_found("File does not exist")); + } + if !target.is_file() { + return Err(AppCommandError::invalid_input("Path is not a file")); + } + + let canonical_root = std::fs::canonicalize(&root).map_err(AppCommandError::io)?; + let canonical_target = std::fs::canonicalize(&target).map_err(AppCommandError::io)?; + if !canonical_target.starts_with(&canonical_root) { + return Err(AppCommandError::invalid_input( + "Path is outside workspace root", + )); + } + + Ok(ResolvedWorkspacePath { + root: canonical_root, + target: canonical_target, + }) +} + +#[cfg(feature = "tauri-runtime")] +fn spawn_code_cli(root: &Path, target: &Path) -> Result<(), std::io::Error> { + let mut command = crate::process::std_command("code"); + command.arg("--new-window").arg(root).arg(target); + command.spawn().map(|_| ()) +} + +#[cfg(all(feature = "tauri-runtime", target_os = "macos"))] +fn spawn_platform_vscode(root: &Path, target: &Path) -> Result<(), std::io::Error> { + let app_cli = Path::new("/Applications/Visual Studio Code.app/Contents/Resources/app/bin/code"); + if app_cli.exists() { + let mut command = crate::process::std_command(app_cli); + command.arg("--new-window").arg(root).arg(target); + if command.spawn().is_ok() { + return Ok(()); + } + } + + let mut command = crate::process::std_command("open"); + command + .arg("-n") + .arg("-a") + .arg("Visual Studio Code") + .arg("--args") + .arg("--new-window") + .arg(root) + .arg(target); + match command.status() { + Ok(status) if status.success() => Ok(()), + Ok(status) => Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + format!("VS Code application could not be opened: {status}"), + )), + Err(err) => Err(err), + } +} + +#[cfg(all(feature = "tauri-runtime", target_os = "windows"))] +fn spawn_platform_vscode(root: &Path, target: &Path) -> Result<(), std::io::Error> { + let mut candidates = Vec::new(); + if let Some(base) = std::env::var_os("LOCALAPPDATA") { + let base = PathBuf::from(base); + candidates.push( + base.join("Programs") + .join("Microsoft VS Code") + .join("Code.exe"), + ); + candidates.push(base.join("Microsoft VS Code").join("Code.exe")); + } + for key in ["ProgramFiles", "ProgramFiles(x86)"] { + if let Some(base) = std::env::var_os(key) { + candidates.push( + PathBuf::from(base) + .join("Microsoft VS Code") + .join("Code.exe"), + ); + } + } + + let mut last_error = None; + for candidate in candidates { + if !candidate.exists() { + continue; + } + let mut command = crate::process::std_command(&candidate); + command.arg("--new-window").arg(root).arg(target); + match command.spawn() { + Ok(_) => return Ok(()), + Err(err) => last_error = Some(err), + } + } + + Err(last_error.unwrap_or_else(|| { + std::io::Error::new(std::io::ErrorKind::NotFound, "VS Code executable not found") + })) +} + +#[cfg(all( + feature = "tauri-runtime", + not(any(target_os = "macos", target_os = "windows")) +))] +fn spawn_platform_vscode(_root: &Path, _target: &Path) -> Result<(), std::io::Error> { + Err(std::io::Error::new( + std::io::ErrorKind::NotFound, + "VS Code executable not found", + )) +} + +#[cfg(feature = "tauri-runtime")] +fn open_path_in_vscode(root: &Path, target: &Path) -> Result<(), AppCommandError> { + match spawn_code_cli(root, target) { + Ok(()) => Ok(()), + Err(code_err) => match spawn_platform_vscode(root, target) { + Ok(()) => Ok(()), + Err(fallback_err) => { + let detail = format!("code: {code_err}; fallback: {fallback_err}"); + if code_err.kind() == std::io::ErrorKind::NotFound + && fallback_err.kind() == std::io::ErrorKind::NotFound + { + Err(AppCommandError::dependency_missing( + "VS Code was not found. Install VS Code and enable the 'code' command in PATH.", + ) + .with_detail(detail)) + } else { + Err(AppCommandError::external_command( + "Failed to open file in VS Code", + detail, + )) + } + } + }, + } +} + pub(crate) async fn load_system_proxy_settings( conn: &DatabaseConnection, ) -> Result { @@ -90,6 +267,72 @@ pub(crate) async fn load_system_language_settings( }) } +pub(crate) async fn load_system_open_target_settings( + conn: &DatabaseConnection, +) -> Result { + let raw = app_metadata_service::get_value(conn, SYSTEM_OPEN_TARGET_SETTINGS_KEY) + .await + .map_err(AppCommandError::from)?; + + let Some(raw) = raw else { + return Ok(SystemOpenTargetSettings::default()); + }; + + let parsed = serde_json::from_str::(&raw).map_err(|e| { + AppCommandError::configuration_invalid("Failed to parse stored open target settings") + .with_detail(e.to_string()) + })?; + Ok(normalize_open_target_settings(parsed)) +} + +pub(crate) async fn update_system_open_target_settings_core( + conn: &DatabaseConnection, + settings: SystemOpenTargetSettings, +) -> Result { + let normalized = normalize_open_target_settings(settings); + let serialized = serde_json::to_string(&normalized).map_err(|e| { + AppCommandError::invalid_input("Failed to serialize open target settings") + .with_detail(e.to_string()) + })?; + + app_metadata_service::upsert_value(conn, SYSTEM_OPEN_TARGET_SETTINGS_KEY, &serialized) + .await + .map_err(AppCommandError::from)?; + + Ok(normalized) +} + +#[cfg(feature = "tauri-runtime")] +pub(crate) async fn open_path_with_target_core( + folder_path: String, + relative_path: String, + target: Option, + conn: &DatabaseConnection, +) -> Result<(), AppCommandError> { + let settings = match target { + Some(target) => SystemOpenTargetSettings { + target, + ..SystemOpenTargetSettings::default() + }, + None => load_system_open_target_settings(conn).await?, + }; + let normalized = normalize_open_target_settings(settings); + + match normalized.target { + SystemOpenTarget::Vscode => { + let resolved_path = resolve_workspace_relative_path(&folder_path, &relative_path)?; + open_path_in_vscode(&resolved_path.root, &resolved_path.target)?; + Ok(()) + } + SystemOpenTarget::FileManager => Err(AppCommandError::invalid_input( + "The open_path_with_target command only supports VS Code. Use file manager actions from the file tree instead.", + )), + SystemOpenTarget::Terminal => Err(AppCommandError::invalid_input( + "The open_path_with_target command does not support opening terminals.", + )), + } +} + #[cfg(feature = "tauri-runtime")] #[cfg_attr(feature = "tauri-runtime", tauri::command)] pub async fn get_system_proxy_settings( @@ -126,6 +369,34 @@ pub async fn get_system_language_settings( load_system_language_settings(&db.conn).await } +#[cfg(feature = "tauri-runtime")] +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn get_system_open_target_settings( + db: State<'_, AppDatabase>, +) -> Result { + load_system_open_target_settings(&db.conn).await +} + +#[cfg(feature = "tauri-runtime")] +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn update_system_open_target_settings( + settings: SystemOpenTargetSettings, + db: State<'_, AppDatabase>, +) -> Result { + update_system_open_target_settings_core(&db.conn, settings).await +} + +#[cfg(feature = "tauri-runtime")] +#[cfg_attr(feature = "tauri-runtime", tauri::command)] +pub async fn open_path_with_target( + folder_path: String, + relative_path: String, + target: Option, + db: State<'_, AppDatabase>, +) -> Result<(), AppCommandError> { + open_path_with_target_core(folder_path, relative_path, target, &db.conn).await +} + #[cfg(feature = "tauri-runtime")] #[cfg_attr(feature = "tauri-runtime", tauri::command)] pub async fn update_system_language_settings( diff --git a/src-tauri/src/commands/windows.rs b/src-tauri/src/commands/windows.rs index 56e988b1..68a14a30 100644 --- a/src-tauri/src/commands/windows.rs +++ b/src-tauri/src/commands/windows.rs @@ -301,13 +301,14 @@ impl Default for CommitWindowState { fn resolve_settings_route(section: Option<&str>) -> &'static str { match section { + Some("general") => "settings/general", Some("appearance") => "settings/appearance", Some("agents") => "settings/agents", Some("mcp") => "settings/mcp", Some("skills") => "settings/skills", Some("shortcuts") => "settings/shortcuts", Some("system") => "settings/system", - _ => "settings/appearance", + _ => "settings/general", } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 7201960f..9ac45baa 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -35,9 +35,8 @@ mod tauri_app { acp as acp_commands, chat_channel as chat_channel_commands, conversations, experts as experts_commands, folder_commands, folders, mcp as mcp_commands, model_provider as model_provider_commands, notification, project_boot, - quick_messages as quick_messages_commands, system_settings, - terminal as terminal_commands, version_control, windows, - workspace_state as workspace_state_commands, + quick_messages as quick_messages_commands, system_settings, terminal as terminal_commands, + version_control, windows, workspace_state as workspace_state_commands, }; use crate::terminal::manager::TerminalManager; use crate::{db, network, process, web}; @@ -66,10 +65,7 @@ mod tauri_app { } let mut tokens: Vec = match std::env::var(ENV_KEY) { - Ok(prev) => prev - .split_whitespace() - .map(str::to_string) - .collect(), + Ok(prev) => prev.split_whitespace().map(str::to_string).collect(), Err(_) => Vec::new(), }; for arg in DISABLE_GPU_ARGS { @@ -366,6 +362,9 @@ mod tauri_app { system_settings::update_system_proxy_settings, system_settings::get_system_language_settings, system_settings::update_system_language_settings, + system_settings::get_system_open_target_settings, + system_settings::update_system_open_target_settings, + system_settings::open_path_with_target, system_settings::get_system_rendering_settings, system_settings::update_system_rendering_settings, version_control::detect_git, diff --git a/src-tauri/src/models/mod.rs b/src-tauri/src/models/mod.rs index 96f30789..c17e65a4 100644 --- a/src-tauri/src/models/mod.rs +++ b/src-tauri/src/models/mod.rs @@ -16,14 +16,14 @@ pub use conversation::{ SidebarData, }; pub use folder::{FolderCommandInfo, FolderDetail, FolderHistoryEntry, OpenedTab}; -pub use quick_message::QuickMessageInfo; pub use message::{ AgentExecutionStats, AgentToolCall, ContentBlock, MessageRole, MessageTurn, TurnRole, TurnUsage, UnifiedMessage, }; +pub use quick_message::QuickMessageInfo; +#[cfg(feature = "tauri-runtime")] +pub use system::{SystemOpenTarget, SystemRenderingSettings}; pub use system::{ GitCredentials, GitDetectResult, GitHubAccountsSettings, GitHubTokenValidation, GitSettings, - SystemLanguageSettings, SystemProxySettings, + SystemLanguageSettings, SystemOpenTargetSettings, SystemProxySettings, }; -#[cfg(feature = "tauri-runtime")] -pub use system::SystemRenderingSettings; diff --git a/src-tauri/src/models/system.rs b/src-tauri/src/models/system.rs index 864b6ae6..35a7c65e 100644 --- a/src-tauri/src/models/system.rs +++ b/src-tauri/src/models/system.rs @@ -37,6 +37,30 @@ pub struct SystemLanguageSettings { pub language: AppLocale, } +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)] +#[serde(rename_all = "snake_case")] +pub enum SystemOpenTarget { + Vscode, + #[default] + FileManager, + Terminal, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default)] +#[serde(rename_all = "snake_case")] +pub enum SystemWebFileOpenMethod { + #[default] + Browser, + Editor, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[serde(default)] +pub struct SystemOpenTargetSettings { + pub target: SystemOpenTarget, + pub web_file_open_method: SystemWebFileOpenMethod, +} + #[cfg(feature = "tauri-runtime")] #[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(default)] diff --git a/src-tauri/src/web/handlers/folders.rs b/src-tauri/src/web/handlers/folders.rs index d615c6b3..d1c99780 100644 --- a/src-tauri/src/web/handlers/folders.rs +++ b/src-tauri/src/web/handlers/folders.rs @@ -223,13 +223,14 @@ pub async fn open_settings_window( Json(params): Json, ) -> Result, AppCommandError> { let route = match params.section.as_deref() { + Some("general") => "settings/general", Some("appearance") => "settings/appearance", Some("agents") => "settings/agents", Some("mcp") => "settings/mcp", Some("skills") => "settings/skills", Some("shortcuts") => "settings/shortcuts", Some("system") => "settings/system", - _ => "settings/appearance", + _ => "settings/general", }; let path = if route == "settings/agents" { diff --git a/src-tauri/src/web/handlers/system_settings.rs b/src-tauri/src/web/handlers/system_settings.rs index b27fd13f..89854dd7 100644 --- a/src-tauri/src/web/handlers/system_settings.rs +++ b/src-tauri/src/web/handlers/system_settings.rs @@ -28,6 +28,11 @@ pub struct UpdateLanguageSettingsParams { pub settings: SystemLanguageSettings, } +#[derive(Deserialize)] +pub struct UpdateOpenTargetSettingsParams { + pub settings: SystemOpenTargetSettings, +} + // --------------------------------------------------------------------------- // Read handlers // --------------------------------------------------------------------------- @@ -48,6 +53,14 @@ pub async fn get_system_language_settings( Ok(Json(settings)) } +pub async fn get_system_open_target_settings( + Extension(state): Extension>, +) -> Result, AppCommandError> { + let db = &state.db; + let settings = settings_commands::load_system_open_target_settings(&db.conn).await?; + Ok(Json(settings)) +} + // --------------------------------------------------------------------------- // Update handlers // --------------------------------------------------------------------------- @@ -98,3 +111,20 @@ pub async fn update_system_language_settings( Ok(Json(settings)) } + +pub async fn update_system_open_target_settings( + Extension(state): Extension>, + Json(params): Json, +) -> Result, AppCommandError> { + let db = &state.db; + let settings = + settings_commands::update_system_open_target_settings_core(&db.conn, params.settings) + .await?; + Ok(Json(settings)) +} + +pub async fn open_path_with_target() -> Result, AppCommandError> { + Err(AppCommandError::configuration_invalid( + "Opening files in a native editor is only available in the desktop app", + )) +} diff --git a/src-tauri/src/web/router.rs b/src-tauri/src/web/router.rs index 804c8ba4..36978911 100644 --- a/src-tauri/src/web/router.rs +++ b/src-tauri/src/web/router.rs @@ -376,6 +376,10 @@ pub fn build_router(state: Arc, token: String, static_dir: std::path:: "/get_system_language_settings", post(handlers::system_settings::get_system_language_settings), ) + .route( + "/get_system_open_target_settings", + post(handlers::system_settings::get_system_open_target_settings), + ) .route( "/update_system_proxy_settings", post(handlers::system_settings::update_system_proxy_settings), @@ -384,6 +388,14 @@ pub fn build_router(state: Arc, token: String, static_dir: std::path:: "/update_system_language_settings", post(handlers::system_settings::update_system_language_settings), ) + .route( + "/update_system_open_target_settings", + post(handlers::system_settings::update_system_open_target_settings), + ) + .route( + "/open_path_with_target", + post(handlers::system_settings::open_path_with_target), + ) // ─── ACP ─── .route( "/acp_get_agent_status", diff --git a/src/app/settings/general/page.tsx b/src/app/settings/general/page.tsx new file mode 100644 index 00000000..fcac692a --- /dev/null +++ b/src/app/settings/general/page.tsx @@ -0,0 +1,5 @@ +import { GeneralSettings } from "@/components/settings/general-settings" + +export default function SettingsGeneralPage() { + return +} diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 1829f604..ae963268 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -7,7 +7,7 @@ export default function SettingsPage() { const router = useRouter() useEffect(() => { - router.replace("/settings/appearance") + router.replace("/settings/general") }, [router]) return null diff --git a/src/components/files/file-workspace-tab-bar.tsx b/src/components/files/file-workspace-tab-bar.tsx index deed2a46..e9606d85 100644 --- a/src/components/files/file-workspace-tab-bar.tsx +++ b/src/components/files/file-workspace-tab-bar.tsx @@ -4,12 +4,19 @@ import { useCallback, useEffect, useRef, useState } from "react" import { Reorder } from "motion/react" import { Code, Eye, ExternalLink, FileText, GitCompare, X } from "lucide-react" import { useTranslations } from "next-intl" -import { openPath } from "@/lib/platform" +import { toast } from "sonner" +import { getSystemOpenTargetSettings, openPathWithTarget } from "@/lib/api" +import { isDesktop, openPath, revealItemInDir } from "@/lib/platform" import { useActiveFolder } from "@/contexts/active-folder-context" +import { useTerminalContext } from "@/contexts/terminal-context" import { useWorkspaceContext } from "@/contexts/workspace-context" import { useShortcutSettings } from "@/hooks/use-shortcut-settings" +import { toErrorMessage } from "@/lib/app-error" import { matchShortcutEvent } from "@/lib/keyboard-shortcuts" +import { isWebFileLanguage } from "@/lib/open-targets" +import { joinFsPath } from "@/lib/path-utils" import { cn } from "@/lib/utils" +import type { SystemOpenTarget, SystemWebFileOpenMethod } from "@/lib/types" import { ContextMenu, ContextMenuContent, @@ -18,8 +25,22 @@ import { ContextMenuTrigger, } from "@/components/ui/context-menu" +function parentDir(filePath: string): string { + const slashIndex = filePath.lastIndexOf("/") + const backslashIndex = filePath.lastIndexOf("\\") + const splitIndex = Math.max(slashIndex, backslashIndex) + if (splitIndex < 0) return filePath + if (splitIndex === 0) return filePath.slice(0, 1) + return filePath.slice(0, splitIndex) +} + +function baseName(path: string): string { + return path.split(/[/\\]/).pop() || path +} + export function FileWorkspaceTabBar() { const t = useTranslations("Folder.fileWorkspace") + const tFileTree = useTranslations("Folder.fileTreeTab") const { mode, activePane, @@ -34,9 +55,12 @@ export function FileWorkspaceTabBar() { toggleFileTabPreview, } = useWorkspaceContext() const { activeFolder: folder } = useActiveFolder() + const { createTerminalInDirectory } = useTerminalContext() const { shortcuts } = useShortcutSettings() const scrollRef = useRef(null) const [isHovered, setIsHovered] = useState(false) + const [webFileOpenMethod, setWebFileOpenMethod] = + useState("browser") const handleWheel = useCallback((e: React.WheelEvent) => { if (e.deltaY !== 0 && scrollRef.current) { @@ -53,6 +77,23 @@ export function FileWorkspaceTabBar() { el?.scrollIntoView({ block: "nearest", inline: "nearest" }) }, [activeFileTabId]) + useEffect(() => { + if (!isDesktop()) return + + let cancelled = false + getSystemOpenTargetSettings() + .then((settings) => { + if (!cancelled) { + setWebFileOpenMethod(settings.web_file_open_method ?? "browser") + } + }) + .catch(() => {}) + + return () => { + cancelled = true + } + }, []) + useEffect(() => { const onKeyDown = (event: KeyboardEvent) => { const shouldHandleShortcut = @@ -85,15 +126,135 @@ export function FileWorkspaceTabBar() { ]) const activeTab = fileTabs.find((tab) => tab.id === activeFileTabId) + const activeFilePath = activeTab?.path ?? null + const activeFolderPath = folder?.path ?? null const canPreview = activeTab?.kind === "file" && activeTab.language === "markdown" - const canOpenInBrowser = - activeTab?.kind === "file" && activeTab.language === "html" + const canOpenWebFile = + isDesktop() && + activeTab?.kind === "file" && + !activeTab.loading && + isWebFileLanguage(activeTab.language) + const canOpenInEditor = + isDesktop() && + activeTab?.kind === "file" && + !activeTab.loading && + !canOpenWebFile && + Boolean(activeFilePath && activeFolderPath) const isPreviewActive = canPreview && activeFileTabId ? previewFileTabIds.has(activeFileTabId) : false + const loadLatestOpenTargetSettings = useCallback(async () => { + const settings = await getSystemOpenTargetSettings() + const nextTarget = settings.target ?? "file_manager" + const nextWebFileOpenMethod = settings.web_file_open_method ?? "browser" + setWebFileOpenMethod(nextWebFileOpenMethod) + return { + target: nextTarget, + webFileOpenMethod: nextWebFileOpenMethod, + } + }, []) + + const openFileWithTarget = useCallback( + async (params: { + folderPath: string + relativePath: string + target: SystemOpenTarget + }) => { + const fullPath = joinFsPath(params.folderPath, params.relativePath) + + switch (params.target) { + case "vscode": + await openPathWithTarget({ + folderPath: params.folderPath, + relativePath: params.relativePath, + target: "vscode", + }) + return + case "file_manager": + await revealItemInDir(fullPath) + return + case "terminal": { + const terminalId = await createTerminalInDirectory( + parentDir(fullPath), + tFileTree("terminalTitle", { name: baseName(params.relativePath) }) + ) + if (!terminalId) { + throw new Error(tFileTree("toasts.openBuiltinTerminalFailed")) + } + return + } + default: { + const exhaustive: never = params.target + return exhaustive + } + } + }, + [createTerminalInDirectory, tFileTree] + ) + + const handleOpenInEditor = useCallback(async () => { + if (!activeFilePath || !activeFolderPath) return + + try { + const { target } = await loadLatestOpenTargetSettings() + await openFileWithTarget({ + folderPath: activeFolderPath, + relativePath: activeFilePath, + target, + }) + } catch (err) { + toast.error(t("openInEditorFailed", { message: toErrorMessage(err) })) + } + }, [ + activeFilePath, + activeFolderPath, + loadLatestOpenTargetSettings, + openFileWithTarget, + t, + ]) + + const handleOpenWebFile = useCallback(async () => { + if (!activeFilePath || !activeFolderPath) return + + let settings: Awaited> + try { + settings = await loadLatestOpenTargetSettings() + } catch (err) { + toast.error(t("openInEditorFailed", { message: toErrorMessage(err) })) + return + } + + const fullPath = joinFsPath(activeFolderPath, activeFilePath) + + if (settings.webFileOpenMethod === "editor") { + try { + await openFileWithTarget({ + folderPath: activeFolderPath, + relativePath: activeFilePath, + target: settings.target, + }) + } catch (err) { + toast.error(t("openInEditorFailed", { message: toErrorMessage(err) })) + } + return + } + + try { + await openPath(fullPath) + } catch (err) { + toast.error(t("openInBrowserFailed", { message: toErrorMessage(err) })) + } + }, [ + activeFilePath, + activeFolderPath, + loadLatestOpenTargetSettings, + openFileWithTarget, + t, + ]) + if (fileTabs.length === 0) { return (
@@ -218,15 +379,28 @@ export function FileWorkspaceTabBar() { )} )} - {canOpenInBrowser && activeTab?.path && folder?.path && ( + {canOpenInEditor && ( + + )} + {canOpenWebFile && activeFilePath && activeFolderPath && ( diff --git a/src/components/layout/aux-panel-file-tree-tab.tsx b/src/components/layout/aux-panel-file-tree-tab.tsx index 69eeb965..ec5a899d 100644 --- a/src/components/layout/aux-panel-file-tree-tab.tsx +++ b/src/components/layout/aux-panel-file-tree-tab.tsx @@ -8,7 +8,7 @@ import { useState, type ReactNode, } from "react" -import { revealItemInDir } from "@/lib/platform" +import { isDesktop, openPath, revealItemInDir } from "@/lib/platform" import ignore from "ignore" import { Check, ChevronRight } from "lucide-react" import { useTranslations } from "next-intl" @@ -30,6 +30,7 @@ import { gitListAllBranches, gitRollbackFile, gitStatus, + openPathWithTarget, readFileForEdit, readFilePreview, openCommitWindow, @@ -38,6 +39,13 @@ import { } from "@/lib/api" import { emitAttachFileToSession } from "@/lib/session-attachment-events" import { ScrollArea } from "@/components/ui/scroll-area" +import { toErrorMessage } from "@/lib/app-error" +import { + getFileTreeOpenTargetItems, + getFileTreeOpenTargetLabelKey, + isWebFilePath, + type OpenTargetRegistryItem, +} from "@/lib/open-targets" import type { FileTreeNode, GitBranchList, GitStatusEntry } from "@/lib/types" import { FileTree, @@ -455,6 +463,118 @@ interface RenderNodeProps { onRefresh: () => void } +interface OpenInSubmenuProps { + itemKind: "file" | "dir" + workspacePath: string + relativePath: string | null + absolutePath: string + terminalDirPath: string + terminalTitleName: string + showBrowserOpen: boolean + onOpenDirInTerminal: (dirPath: string, fileName: string) => Promise +} + +function OpenInSubmenu({ + itemKind, + workspacePath, + relativePath, + absolutePath, + terminalDirPath, + terminalTitleName, + showBrowserOpen, + onOpenDirInTerminal, +}: OpenInSubmenuProps) { + const t = useTranslations("Folder.fileTreeTab") + const targets = getFileTreeOpenTargetItems(itemKind) + + const handleOpenBrowser = useCallback(async () => { + try { + await openPath(absolutePath) + } catch (error) { + toast.error(t("toasts.openInBrowserFailed"), { + description: toErrorMessage(error), + }) + } + }, [absolutePath, t]) + + const handleOpenTarget = useCallback( + async (target: OpenTargetRegistryItem) => { + switch (target.id) { + case "vscode": + if (!relativePath) return + try { + await openPathWithTarget({ + folderPath: workspacePath, + relativePath, + target: "vscode", + }) + } catch (error) { + toast.error(t("toasts.openInEditorFailed"), { + description: toErrorMessage(error), + }) + } + return + case "file_manager": + try { + await revealItemInDir(absolutePath) + } catch (error) { + const message = + error instanceof Error ? error.message : String(error) + toast.error(t("toasts.openDirectoryFailed"), { + description: message, + }) + } + return + case "terminal": + await onOpenDirInTerminal(terminalDirPath, terminalTitleName) + return + default: { + const exhaustive: never = target.id + return exhaustive + } + } + }, + [ + absolutePath, + onOpenDirInTerminal, + relativePath, + t, + terminalDirPath, + terminalTitleName, + workspacePath, + ] + ) + + if (targets.length === 0 && !showBrowserOpen) return null + + return ( + + {t("openIn")} + + {showBrowserOpen && ( + { + void handleOpenBrowser() + }} + > + {t("openInBrowser")} + + )} + {targets.map((target) => ( + { + void handleOpenTarget(target) + }} + > + {t(getFileTreeOpenTargetLabelKey(target))} + + ))} + + + ) +} + function RenderNode({ node, expandedPaths, @@ -485,17 +605,6 @@ function RenderNode({ const isGitignoreIgnored = ancestorGitignoreIgnored || gitignoreIgnoredPaths.has(node.path) - const systemExplorerLabel = - typeof navigator === "undefined" - ? t("openInFileManager") - : (() => { - const platform = - `${navigator.platform} ${navigator.userAgent}`.toLowerCase() - if (platform.includes("mac")) return t("openInFinder") - if (platform.includes("win")) return t("openInExplorer") - return t("openInFileManager") - })() - if (node.kind === "file") { const gitStatusCode = gitStatusByPath.get(node.path) ?? (ancestorUntracked ? "??" : undefined) @@ -511,15 +620,6 @@ function RenderNode({ }) } - const handleOpenInSystemExplorer = async () => { - try { - await revealItemInDir(absolutePath) - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - toast.error(t("toasts.openDirectoryFailed"), { description: message }) - } - } - return ( @@ -605,21 +705,16 @@ function RenderNode({ {t("reloadFromDisk")} - - {t("openIn")} - - void handleOpenInSystemExplorer()} - > - {systemExplorerLabel} - - void onOpenDirInTerminal(dirPath, node.name)} - > - {t("openInTerminal")} - - - + onRequestDelete(node)} variant="destructive" @@ -648,15 +743,6 @@ function RenderNode({ }) } - const handleOpenDirInSystemExplorer = async () => { - try { - await revealItemInDir(absolutePath) - } catch (error) { - const message = error instanceof Error ? error.message : String(error) - toast.error(t("toasts.openDirectoryFailed"), { description: message }) - } - } - return ( @@ -765,21 +851,16 @@ function RenderNode({ onRequestRename(node)}> {tCommon("rename")} - - {t("openIn")} - - void handleOpenDirInSystemExplorer()} - > - {systemExplorerLabel} - - void onOpenDirInTerminal(absolutePath, node.name)} - > - {t("openInTerminal")} - - - + {t("reloadFromDisk")} @@ -1963,17 +2044,6 @@ export function FileTreeTab() { return baseName(folder.path) }, [folder?.path, t]) - const systemExplorerLabel = - typeof navigator === "undefined" - ? t("openInFileManager") - : (() => { - const platform = - `${navigator.platform} ${navigator.userAgent}`.toLowerCase() - if (platform.includes("mac")) return t("openInFinder") - if (platform.includes("win")) return t("openInExplorer") - return t("openInFileManager") - })() - const rootTarget: FileActionTarget = useMemo( () => ({ kind: "dir", path: "", name: rootNodeName }), [rootNodeName] @@ -2264,30 +2334,16 @@ export function FileTreeTab() { > {t("reloadFromDisk")} - - - {t("openIn")} - - - { - void revealItemInDir(folder.path) - }} - > - {systemExplorerLabel} - - { - void handleOpenDirInTerminal( - folder.path, - rootNodeName - ) - }} - > - {t("openInTerminal")} - - - + )} diff --git a/src/components/settings/general-settings.tsx b/src/components/settings/general-settings.tsx new file mode 100644 index 00000000..ca5a279e --- /dev/null +++ b/src/components/settings/general-settings.tsx @@ -0,0 +1,209 @@ +"use client" + +import { useCallback, useEffect, useState } from "react" +import { ExternalLink, Loader2 } from "lucide-react" +import { useTranslations } from "next-intl" +import { toast } from "sonner" +import { ScrollArea } from "@/components/ui/scroll-area" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { + getSystemOpenTargetSettings, + updateSystemOpenTargetSettings, +} from "@/lib/api" +import { toErrorMessage } from "@/lib/app-error" +import { OPEN_TARGET_REGISTRY, isSystemOpenTarget } from "@/lib/open-targets" +import { isDesktop } from "@/lib/platform" +import type { SystemOpenTarget, SystemWebFileOpenMethod } from "@/lib/types" + +function isSystemWebFileOpenMethod( + value: string +): value is SystemWebFileOpenMethod { + return value === "browser" || value === "editor" +} + +export function GeneralSettings() { + const t = useTranslations("GeneralSettings") + const [loading, setLoading] = useState(true) + const [savingOpenTarget, setSavingOpenTarget] = useState(false) + const [savingWebFileOpenMethod, setSavingWebFileOpenMethod] = useState(false) + const [openTarget, setOpenTarget] = useState("file_manager") + const [webFileOpenMethod, setWebFileOpenMethod] = + useState("browser") + const [loadError, setLoadError] = useState(null) + const savingOpenSettings = savingOpenTarget || savingWebFileOpenMethod + + const loadSettings = useCallback(async () => { + setLoading(true) + setLoadError(null) + + try { + const settings = await getSystemOpenTargetSettings() + setOpenTarget(settings.target) + setWebFileOpenMethod(settings.web_file_open_method ?? "browser") + } catch (err) { + const message = toErrorMessage(err) + setLoadError(message) + } finally { + setLoading(false) + } + }, []) + + useEffect(() => { + void loadSettings() + }, [loadSettings]) + + const saveOpenTarget = useCallback( + async (target: SystemOpenTarget, previous: SystemOpenTarget) => { + setSavingOpenTarget(true) + + try { + const next = await updateSystemOpenTargetSettings({ + target, + web_file_open_method: webFileOpenMethod, + }) + setOpenTarget(next.target) + setWebFileOpenMethod(next.web_file_open_method) + } catch (err) { + setOpenTarget(previous) + const message = toErrorMessage(err) + toast.error(t("openTargetSaveFailed", { message })) + } finally { + setSavingOpenTarget(false) + } + }, + [t, webFileOpenMethod] + ) + + const saveWebFileOpenMethod = useCallback( + async ( + method: SystemWebFileOpenMethod, + previous: SystemWebFileOpenMethod + ) => { + setSavingWebFileOpenMethod(true) + + try { + const next = await updateSystemOpenTargetSettings({ + target: openTarget, + web_file_open_method: method, + }) + setOpenTarget(next.target) + setWebFileOpenMethod(next.web_file_open_method) + } catch (err) { + setWebFileOpenMethod(previous) + const message = toErrorMessage(err) + toast.error(t("webFileOpenMethodSaveFailed", { message })) + } finally { + setSavingWebFileOpenMethod(false) + } + }, + [openTarget, t] + ) + + if (loading) { + return ( +
+ + {t("loading")} +
+ ) + } + + return ( + +
+
+

{t("sectionTitle")}

+

+ {t("sectionDescription")} +

+
+ +
+
+ +

{t("openTargetTitle")}

+
+ + {loadError && ( +
+ {t("loadFailed", { message: loadError })} +
+ )} + +
+
+

{t("defaultOpenTarget")}

+

+ {t("defaultOpenTargetDescription")} +

+
+ +
+ +
+
+

{t("webFileOpenMethod")}

+

+ {t("webFileOpenMethodDescription")} +

+
+ +
+ + {!isDesktop() && ( +

+ {t("openTargetDesktopOnly")} +

+ )} +
+
+
+ ) +} diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index 3321619b..c85425b7 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -20,6 +20,7 @@ import { PlugZap, Server, Settings, + SlidersHorizontal, Sparkles, } from "lucide-react" import { useTranslations } from "next-intl" @@ -36,6 +37,7 @@ import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet" interface SettingsNavItem { href: string labelKey: + | "general" | "appearance" | "agents" | "model_providers" @@ -52,6 +54,11 @@ interface SettingsNavItem { } const SETTINGS_NAV_ITEMS: SettingsNavItem[] = [ + { + href: "/settings/general", + labelKey: "general", + icon: SlidersHorizontal, + }, { href: "/settings/appearance", labelKey: "appearance", diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 66b67157..4e05aada 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -37,6 +37,7 @@ "title": "الإعدادات", "preferences": "التفضيلات", "nav": { + "general": "عام", "appearance": "المظهر", "agents": "الوكلاء", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "رسائل سريعة" } }, + "GeneralSettings": { + "loading": "جارٍ التحميل...", + "sectionTitle": "عام", + "sectionDescription": "اضبط التفضيلات العامة للتطبيق.", + "loadFailed": "فشل التحميل: {message}", + "openTargetTitle": "فتح الملفات", + "defaultOpenTarget": "هدف الفتح الافتراضي", + "defaultOpenTargetDescription": "الموقع الافتراضي لفتح الملفات والمجلدات", + "openTargetDesktopOnly": "فتح الملفات في محرر أصلي متاح فقط في تطبيق سطح المكتب.", + "openTargetSaveFailed": "فشل حفظ هدف الفتح: {message}", + "webFileOpenMethod": "طريقة فتح ملفات الويب", + "webFileOpenMethodDescription": "الطريقة الافتراضية لفتح ملفات HTML وملفات الويب الأخرى", + "webFileOpenMethodSaveFailed": "فشل حفظ إعدادات فتح ملفات الويب: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "مدير الملفات", + "terminal": "الطرفية" + }, + "webFileOpenMethods": { + "browser": "فتح في المتصفح", + "editor": "استخدام هدف الفتح الافتراضي" + } + }, "AppearanceSettings": { "sectionTitle": "مظهر السمة", "sectionDescription": "اختر الفاتح أو الداكن أو اتباع النظام. يتم حفظ الإعدادات تلقائيًا.", @@ -109,6 +133,14 @@ "proxyRequired": "عنوان URL للوكيل مطلوب عند تفعيل الوكيل", "saveSuccess": "تم حفظ إعدادات وكيل النظام", "saveFailed": "فشل الحفظ: {message}", + "openTargetTitle": "فتح الملفات", + "defaultOpenTarget": "هدف الفتح الافتراضي", + "defaultOpenTargetDescription": "الموقع الافتراضي لفتح الملفات والمجلدات", + "openTargetDesktopOnly": "فتح الملفات في محرر أصلي متاح فقط في تطبيق سطح المكتب.", + "openTargetSaveFailed": "فشل حفظ هدف الفتح: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "اللغة", "languageDescription": "حدد لغة التطبيق. عند اتباع لغة النظام، ستعود اللغات غير المدعومة إلى الإنجليزية.", "appLanguage": "لغة التطبيق", @@ -983,7 +1015,10 @@ "closeOthers": "إغلاق البقية", "closeAll": "إغلاق الكل", "preview": "معاينة", - "editSource": "تحرير المصدر" + "editSource": "تحرير المصدر", + "openInEditor": "فتح في المحرر", + "openInEditorFailed": "فشل الفتح في المحرر: {message}", + "openInBrowserFailed": "فشل الفتح في المتصفح: {message}" }, "terminal": { "rename": "إعادة تسمية", @@ -1377,6 +1412,8 @@ "newFile": "ملف", "newDirectory": "مجلد", "openIn": "فتح في", + "openInBrowser": "فتح في المتصفح", + "openInEditor": "فتح في المحرر", "openInTerminal": "فتح في الطرفية", "actions": { "select": "تحديد", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "فشل فتح المجلد", + "openInEditorFailed": "فشل الفتح في المحرر", "openBuiltinTerminalFailed": "تعذر فتح الطرفية المدمجة", "openCommitWindowFailed": "فشل فتح نافذة الالتزام", "noAddableFilesInDir": "لا توجد ملفات متغيرة في هذا المجلد يمكن إضافتها إلى VCS", @@ -1406,7 +1444,8 @@ "savedAsCopy": "تم الحفظ كنسخة", "saveCopyFailed": "فشل الحفظ كنسخة", "watchStartFailed": "فشل بدء مراقبة الملفات", - "createFailed": "فشل في الإنشاء" + "createFailed": "فشل في الإنشاء", + "openInBrowserFailed": "فشل الفتح في المتصفح" }, "createDialog": { "newFile": "ملف جديد", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 70220a0d..52ab4f9d 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -37,6 +37,7 @@ "title": "Einstellungen", "preferences": "Präferenzen", "nav": { + "general": "Allgemein", "appearance": "Darstellung", "agents": "Agenten", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "Schnellnachrichten" } }, + "GeneralSettings": { + "loading": "Wird geladen...", + "sectionTitle": "Allgemein", + "sectionDescription": "Allgemeine App-Einstellungen konfigurieren.", + "loadFailed": "Laden fehlgeschlagen: {message}", + "openTargetTitle": "Dateien öffnen", + "defaultOpenTarget": "Standardziel zum Öffnen", + "defaultOpenTargetDescription": "Standardort zum Öffnen von Dateien und Ordnern", + "openTargetDesktopOnly": "Das Öffnen in einem nativen Editor ist nur in der Desktop-App verfügbar.", + "openTargetSaveFailed": "Öffnungsziel konnte nicht gespeichert werden: {message}", + "webFileOpenMethod": "Webdateien öffnen", + "webFileOpenMethodDescription": "Standardmethode zum Öffnen von HTML- und anderen Webdateien", + "webFileOpenMethodSaveFailed": "Einstellungen zum Öffnen von Webdateien konnten nicht gespeichert werden: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Dateimanager", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Im Browser öffnen", + "editor": "Standard-Öffnungsziel verwenden" + } + }, "AppearanceSettings": { "sectionTitle": "Design-Erscheinungsbild", "sectionDescription": "Wähle hell, dunkel oder Systemvorgabe. Einstellungen werden automatisch gespeichert.", @@ -109,6 +133,14 @@ "proxyRequired": "Bei aktiviertem Proxy ist eine Proxy-URL erforderlich", "saveSuccess": "System-Proxy-Einstellungen wurden gespeichert", "saveFailed": "Speichern fehlgeschlagen: {message}", + "openTargetTitle": "Dateien öffnen", + "defaultOpenTarget": "Standardziel zum Öffnen", + "defaultOpenTargetDescription": "Standardort zum Öffnen von Dateien und Ordnern", + "openTargetDesktopOnly": "Das Öffnen in einem nativen Editor ist nur in der Desktop-App verfügbar.", + "openTargetSaveFailed": "Öffnungsziel konnte nicht gespeichert werden: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "Sprache", "languageDescription": "Lege die App-Sprache fest. Bei Systemsprache wird bei nicht unterstützten Sprachen auf Englisch zurückgefallen.", "appLanguage": "App-Sprache", @@ -983,7 +1015,10 @@ "closeOthers": "Andere schließen", "closeAll": "Alle schließen", "preview": "Vorschau", - "editSource": "Quelle bearbeiten" + "editSource": "Quelle bearbeiten", + "openInEditor": "Im Editor öffnen", + "openInEditorFailed": "Öffnen im Editor fehlgeschlagen: {message}", + "openInBrowserFailed": "Konnte nicht im Browser geöffnet werden: {message}" }, "terminal": { "rename": "Umbenennen", @@ -1377,6 +1412,8 @@ "newFile": "Datei", "newDirectory": "Verzeichnis", "openIn": "Öffnen in", + "openInBrowser": "Im Browser öffnen", + "openInEditor": "Im Editor öffnen", "openInTerminal": "Im Terminal öffnen", "actions": { "select": "Auswählen", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "Verzeichnis konnte nicht geöffnet werden", + "openInEditorFailed": "Öffnen im Editor fehlgeschlagen", "openBuiltinTerminalFailed": "Integriertes Terminal konnte nicht geöffnet werden", "openCommitWindowFailed": "Commit-Fenster konnte nicht geöffnet werden", "noAddableFilesInDir": "Keine geänderten Dateien in diesem Verzeichnis können zu VCS hinzugefügt werden", @@ -1406,7 +1444,8 @@ "savedAsCopy": "Als Kopie gespeichert", "saveCopyFailed": "Speichern als Kopie fehlgeschlagen", "watchStartFailed": "Dateiwatch konnte nicht gestartet werden", - "createFailed": "Erstellen fehlgeschlagen" + "createFailed": "Erstellen fehlgeschlagen", + "openInBrowserFailed": "Konnte nicht im Browser geöffnet werden" }, "createDialog": { "newFile": "Neue Datei", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index 524c4543..ca5f942e 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -37,6 +37,7 @@ "title": "Settings", "preferences": "Preferences", "nav": { + "general": "General", "appearance": "Appearance", "agents": "Agents", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "Quick Messages" } }, + "GeneralSettings": { + "loading": "Loading...", + "sectionTitle": "General", + "sectionDescription": "Configure default app preferences.", + "loadFailed": "Load failed: {message}", + "openTargetTitle": "File opening", + "defaultOpenTarget": "Default open target", + "defaultOpenTargetDescription": "Default location for opening files and folders", + "openTargetDesktopOnly": "Opening files in a native editor is available in the desktop app.", + "openTargetSaveFailed": "Failed to save open target settings: {message}", + "webFileOpenMethod": "Web file opening", + "webFileOpenMethodDescription": "Default method for opening HTML and other web files", + "webFileOpenMethodSaveFailed": "Failed to save web file opening settings: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "File manager", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Open in browser", + "editor": "Use default open target" + } + }, "AppearanceSettings": { "sectionTitle": "Theme Appearance", "sectionDescription": "Choose light, dark, or follow system. Settings are saved automatically.", @@ -109,6 +133,14 @@ "proxyRequired": "Proxy URL is required when proxy is enabled", "saveSuccess": "System proxy settings saved", "saveFailed": "Save failed: {message}", + "openTargetTitle": "File opening", + "defaultOpenTarget": "Default open target", + "defaultOpenTargetDescription": "Default location for opening files and folders", + "openTargetDesktopOnly": "Opening files in a native editor is available in the desktop app.", + "openTargetSaveFailed": "Failed to save open target settings: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "Language", "languageDescription": "Set app language. When following system locale, unsupported languages fall back to English.", "appLanguage": "App language", @@ -983,7 +1015,10 @@ "closeOthers": "Close Others", "closeAll": "Close All", "preview": "Preview", - "editSource": "Edit Source" + "editSource": "Edit Source", + "openInEditor": "Open in editor", + "openInEditorFailed": "Failed to open in editor: {message}", + "openInBrowserFailed": "Failed to open in browser: {message}" }, "terminal": { "rename": "Rename", @@ -1377,6 +1412,8 @@ "newFile": "File", "newDirectory": "Directory", "openIn": "Open in", + "openInBrowser": "Open in browser", + "openInEditor": "Open in editor", "openInTerminal": "Open in terminal", "actions": { "select": "Select", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "Failed to open directory", + "openInEditorFailed": "Failed to open in editor", "openBuiltinTerminalFailed": "Unable to open built-in terminal", "openCommitWindowFailed": "Failed to open commit window", "noAddableFilesInDir": "No changed files in this directory can be added to VCS", @@ -1406,7 +1444,8 @@ "savedAsCopy": "Saved as a copy", "saveCopyFailed": "Failed to save as copy", "watchStartFailed": "Failed to start file watch", - "createFailed": "Failed to create" + "createFailed": "Failed to create", + "openInBrowserFailed": "Failed to open in browser" }, "createDialog": { "newFile": "New file", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index d13f2f50..951b2eec 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -37,6 +37,7 @@ "title": "Configuración", "preferences": "Preferencias", "nav": { + "general": "General", "appearance": "Apariencia", "agents": "Agentes", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "Mensajes rápidos" } }, + "GeneralSettings": { + "loading": "Cargando...", + "sectionTitle": "General", + "sectionDescription": "Configura las preferencias generales de la aplicación.", + "loadFailed": "Error al cargar: {message}", + "openTargetTitle": "Apertura de archivos", + "defaultOpenTarget": "Destino predeterminado de apertura", + "defaultOpenTargetDescription": "Ubicación predeterminada para abrir archivos y carpetas", + "openTargetDesktopOnly": "Abrir archivos en un editor nativo solo está disponible en la app de escritorio.", + "openTargetSaveFailed": "No se pudo guardar el destino de apertura: {message}", + "webFileOpenMethod": "Apertura de archivos web", + "webFileOpenMethodDescription": "Método predeterminado para abrir HTML y otros archivos web", + "webFileOpenMethodSaveFailed": "No se pudo guardar la configuración de apertura de archivos web: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Gestor de archivos", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Abrir en el navegador", + "editor": "Usar destino de apertura predeterminado" + } + }, "AppearanceSettings": { "sectionTitle": "Apariencia del tema", "sectionDescription": "Elige claro, oscuro o seguir el sistema. La configuración se guarda automáticamente.", @@ -109,6 +133,14 @@ "proxyRequired": "Se requiere URL del proxy cuando el proxy está activado", "saveSuccess": "La configuración del proxy del sistema se guardó", "saveFailed": "Error al guardar: {message}", + "openTargetTitle": "Apertura de archivos", + "defaultOpenTarget": "Destino predeterminado de apertura", + "defaultOpenTargetDescription": "Ubicación predeterminada para abrir archivos y carpetas", + "openTargetDesktopOnly": "Abrir archivos en un editor nativo solo está disponible en la app de escritorio.", + "openTargetSaveFailed": "No se pudo guardar el destino de apertura: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "Idioma", "languageDescription": "Configura el idioma de la app. Al seguir el idioma del sistema, los no compatibles vuelven a inglés.", "appLanguage": "Idioma de la app", @@ -983,7 +1015,10 @@ "closeOthers": "Cerrar otros", "closeAll": "Cerrar todo", "preview": "Vista previa", - "editSource": "Editar fuente" + "editSource": "Editar fuente", + "openInEditor": "Abrir en editor", + "openInEditorFailed": "No se pudo abrir en el editor: {message}", + "openInBrowserFailed": "No se pudo abrir en el navegador: {message}" }, "terminal": { "rename": "Renombrar", @@ -1377,6 +1412,8 @@ "newFile": "Archivo", "newDirectory": "Directorio", "openIn": "Abrir en", + "openInBrowser": "Abrir en el navegador", + "openInEditor": "Abrir en editor", "openInTerminal": "Abrir en terminal", "actions": { "select": "Seleccionar", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "No se pudo abrir el directorio", + "openInEditorFailed": "No se pudo abrir en el editor", "openBuiltinTerminalFailed": "No se pudo abrir la terminal integrada", "openCommitWindowFailed": "No se pudo abrir la ventana de commit", "noAddableFilesInDir": "No hay archivos modificados en este directorio que se puedan añadir a VCS", @@ -1406,7 +1444,8 @@ "savedAsCopy": "Guardado como copia", "saveCopyFailed": "No se pudo guardar como copia", "watchStartFailed": "No se pudo iniciar la vigilancia de archivos", - "createFailed": "Error al crear" + "createFailed": "Error al crear", + "openInBrowserFailed": "No se pudo abrir en el navegador" }, "createDialog": { "newFile": "Nuevo archivo", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index c5862a32..6d210cd3 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -37,6 +37,7 @@ "title": "Paramètres", "preferences": "Préférences", "nav": { + "general": "Général", "appearance": "Apparence", "agents": "Agents IA", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "Messages rapides" } }, + "GeneralSettings": { + "loading": "Chargement...", + "sectionTitle": "Général", + "sectionDescription": "Configurez les préférences générales de l’application.", + "loadFailed": "Échec du chargement : {message}", + "openTargetTitle": "Ouverture de fichiers", + "defaultOpenTarget": "Cible d’ouverture par défaut", + "defaultOpenTargetDescription": "Emplacement par défaut pour ouvrir les fichiers et dossiers", + "openTargetDesktopOnly": "L’ouverture dans un éditeur natif est disponible uniquement dans l’app de bureau.", + "openTargetSaveFailed": "Échec de l’enregistrement de la cible d’ouverture : {message}", + "webFileOpenMethod": "Ouverture des fichiers web", + "webFileOpenMethodDescription": "Méthode par défaut pour ouvrir les fichiers HTML et autres fichiers web", + "webFileOpenMethodSaveFailed": "Échec de l’enregistrement de l’ouverture des fichiers web : {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Gestionnaire de fichiers", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Ouvrir dans le navigateur", + "editor": "Utiliser la cible d’ouverture par défaut" + } + }, "AppearanceSettings": { "sectionTitle": "Apparence du thème", "sectionDescription": "Choisissez clair, sombre ou suivre le système. Les paramètres sont enregistrés automatiquement.", @@ -109,6 +133,14 @@ "proxyRequired": "L’URL du proxy est requise lorsque le proxy est activé", "saveSuccess": "Les paramètres du proxy système ont été enregistrés", "saveFailed": "Échec de l’enregistrement : {message}", + "openTargetTitle": "Ouverture de fichiers", + "defaultOpenTarget": "Cible d’ouverture par défaut", + "defaultOpenTargetDescription": "Emplacement par défaut pour ouvrir les fichiers et dossiers", + "openTargetDesktopOnly": "L’ouverture dans un éditeur natif est disponible uniquement dans l’app de bureau.", + "openTargetSaveFailed": "Échec de l’enregistrement de la cible d’ouverture : {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "Langue", "languageDescription": "Définissez la langue de l’app. En mode système, les langues non prises en charge reviennent à l’anglais.", "appLanguage": "Langue de l’app", @@ -983,7 +1015,10 @@ "closeOthers": "Fermer les autres", "closeAll": "Tout fermer", "preview": "Aperçu", - "editSource": "Modifier la source" + "editSource": "Modifier la source", + "openInEditor": "Ouvrir dans l’éditeur", + "openInEditorFailed": "Échec de l’ouverture dans l’éditeur : {message}", + "openInBrowserFailed": "Échec de l’ouverture dans le navigateur : {message}" }, "terminal": { "rename": "Renommer", @@ -1377,6 +1412,8 @@ "newFile": "Fichier", "newDirectory": "Répertoire", "openIn": "Ouvrir dans", + "openInBrowser": "Ouvrir dans le navigateur", + "openInEditor": "Ouvrir dans l’éditeur", "openInTerminal": "Ouvrir dans le terminal", "actions": { "select": "Sélectionner", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "Échec de l'ouverture du dossier", + "openInEditorFailed": "Échec de l’ouverture dans l’éditeur", "openBuiltinTerminalFailed": "Impossible d'ouvrir le terminal intégré", "openCommitWindowFailed": "Échec de l'ouverture de la fenêtre de commit", "noAddableFilesInDir": "Aucun fichier modifié de ce dossier ne peut être ajouté au VCS", @@ -1406,7 +1444,8 @@ "savedAsCopy": "Enregistré en copie", "saveCopyFailed": "Échec de l'enregistrement en copie", "watchStartFailed": "Échec du démarrage de la surveillance de fichiers", - "createFailed": "Échec de la création" + "createFailed": "Échec de la création", + "openInBrowserFailed": "Échec de l’ouverture dans le navigateur" }, "createDialog": { "newFile": "Nouveau fichier", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index 30b1df59..8de05eb0 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -37,6 +37,7 @@ "title": "設定", "preferences": "環境設定", "nav": { + "general": "一般", "appearance": "外観", "agents": "エージェント", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "クイックメッセージ" } }, + "GeneralSettings": { + "loading": "読み込み中...", + "sectionTitle": "一般", + "sectionDescription": "アプリの一般設定を構成します。", + "loadFailed": "読み込みに失敗しました: {message}", + "openTargetTitle": "ファイルを開く", + "defaultOpenTarget": "デフォルトの開き先", + "defaultOpenTargetDescription": "ファイルとフォルダーを開くデフォルトの場所", + "openTargetDesktopOnly": "ネイティブエディターでファイルを開く機能はデスクトップアプリでのみ利用できます。", + "openTargetSaveFailed": "開き先設定の保存に失敗しました: {message}", + "webFileOpenMethod": "Web ファイルの開き方", + "webFileOpenMethodDescription": "HTML などのファイルを開く既定の方法", + "webFileOpenMethodSaveFailed": "Web ファイルの開き方設定の保存に失敗しました: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "ファイルマネージャー", + "terminal": "ターミナル" + }, + "webFileOpenMethods": { + "browser": "ブラウザーで開く", + "editor": "デフォルトの開き先を使用" + } + }, "AppearanceSettings": { "sectionTitle": "テーマ外観", "sectionDescription": "ライト、ダーク、またはシステム追従を選択できます。設定は自動保存されます。", @@ -109,6 +133,14 @@ "proxyRequired": "プロキシ有効時はプロキシ URL が必要です", "saveSuccess": "システムプロキシ設定を保存しました", "saveFailed": "保存に失敗しました: {message}", + "openTargetTitle": "ファイルを開く", + "defaultOpenTarget": "デフォルトの開き先", + "defaultOpenTargetDescription": "ファイルとフォルダーを開くデフォルトの場所", + "openTargetDesktopOnly": "ネイティブエディターでファイルを開く機能はデスクトップアプリでのみ利用できます。", + "openTargetSaveFailed": "開き先設定の保存に失敗しました: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "言語", "languageDescription": "アプリの言語を設定します。システムに従う場合、未対応言語は英語にフォールバックします。", "appLanguage": "アプリ言語", @@ -983,7 +1015,10 @@ "closeOthers": "他を閉じる", "closeAll": "すべて閉じる", "preview": "プレビュー", - "editSource": "ソースを編集" + "editSource": "ソースを編集", + "openInEditor": "エディターで開く", + "openInEditorFailed": "エディターで開けませんでした: {message}", + "openInBrowserFailed": "ブラウザーで開けませんでした: {message}" }, "terminal": { "rename": "名前を変更", @@ -1377,6 +1412,8 @@ "newFile": "ファイル", "newDirectory": "ディレクトリ", "openIn": "で開く", + "openInBrowser": "ブラウザーで開く", + "openInEditor": "エディターで開く", "openInTerminal": "ターミナルで開く", "actions": { "select": "選択", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "ディレクトリを開けませんでした", + "openInEditorFailed": "エディターで開けませんでした", "openBuiltinTerminalFailed": "内蔵ターミナルを開けませんでした", "openCommitWindowFailed": "コミットウィンドウを開けませんでした", "noAddableFilesInDir": "このディレクトリには VCS に追加できる変更ファイルがありません", @@ -1406,7 +1444,8 @@ "savedAsCopy": "コピーとして保存しました", "saveCopyFailed": "コピーとして保存できませんでした", "watchStartFailed": "ファイル監視の開始に失敗しました", - "createFailed": "作成に失敗しました" + "createFailed": "作成に失敗しました", + "openInBrowserFailed": "ブラウザーで開けませんでした" }, "createDialog": { "newFile": "新規ファイル", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 5ee5b3a2..144a9d6e 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -37,6 +37,7 @@ "title": "설정", "preferences": "환경설정", "nav": { + "general": "일반", "appearance": "외관", "agents": "에이전트", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "빠른 메시지" } }, + "GeneralSettings": { + "loading": "로딩 중...", + "sectionTitle": "일반", + "sectionDescription": "앱의 일반 환경설정을 구성합니다.", + "loadFailed": "불러오기 실패: {message}", + "openTargetTitle": "파일 열기", + "defaultOpenTarget": "기본 열기 대상", + "defaultOpenTargetDescription": "파일과 폴더를 열 기본 위치", + "openTargetDesktopOnly": "네이티브 편집기에서 파일 열기는 데스크톱 앱에서만 사용할 수 있습니다.", + "openTargetSaveFailed": "열기 대상 설정 저장 실패: {message}", + "webFileOpenMethod": "웹 파일 열기 방식", + "webFileOpenMethodDescription": "HTML 등 파일의 기본 열기 방식", + "webFileOpenMethodSaveFailed": "웹 파일 열기 설정 저장 실패: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "파일 관리자", + "terminal": "터미널" + }, + "webFileOpenMethods": { + "browser": "브라우저에서 열기", + "editor": "기본 열기 대상 사용" + } + }, "AppearanceSettings": { "sectionTitle": "테마 모양", "sectionDescription": "라이트, 다크 또는 시스템 설정을 선택할 수 있습니다. 설정은 자동으로 저장됩니다.", @@ -109,6 +133,14 @@ "proxyRequired": "프록시를 활성화하면 프록시 URL이 필요합니다", "saveSuccess": "시스템 프록시 설정이 저장되었습니다", "saveFailed": "저장 실패: {message}", + "openTargetTitle": "파일 열기", + "defaultOpenTarget": "기본 열기 대상", + "defaultOpenTargetDescription": "파일과 폴더를 열 기본 위치", + "openTargetDesktopOnly": "네이티브 편집기에서 파일 열기는 데스크톱 앱에서만 사용할 수 있습니다.", + "openTargetSaveFailed": "열기 대상 설정 저장 실패: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "언어", "languageDescription": "앱 언어를 설정합니다. 시스템 언어를 따를 때 지원되지 않는 언어는 영어로 대체됩니다.", "appLanguage": "앱 언어", @@ -983,7 +1015,10 @@ "closeOthers": "다른 항목 닫기", "closeAll": "모두 닫기", "preview": "미리보기", - "editSource": "소스 편집" + "editSource": "소스 편집", + "openInEditor": "편집기에서 열기", + "openInEditorFailed": "편집기에서 열기 실패: {message}", + "openInBrowserFailed": "브라우저에서 열지 못했습니다: {message}" }, "terminal": { "rename": "이름 변경", @@ -1377,6 +1412,8 @@ "newFile": "파일", "newDirectory": "디렉터리", "openIn": "열기", + "openInBrowser": "브라우저에서 열기", + "openInEditor": "편집기에서 열기", "openInTerminal": "터미널에서 열기", "actions": { "select": "선택", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "디렉터리를 열지 못했습니다", + "openInEditorFailed": "편집기에서 열기 실패", "openBuiltinTerminalFailed": "내장 터미널을 열 수 없습니다", "openCommitWindowFailed": "커밋 창을 열지 못했습니다", "noAddableFilesInDir": "이 디렉터리에는 VCS에 추가할 수 있는 변경 파일이 없습니다", @@ -1406,7 +1444,8 @@ "savedAsCopy": "사본으로 저장했습니다", "saveCopyFailed": "사본으로 저장하지 못했습니다", "watchStartFailed": "파일 감시 시작에 실패했습니다", - "createFailed": "생성 실패" + "createFailed": "생성 실패", + "openInBrowserFailed": "브라우저에서 열지 못했습니다" }, "createDialog": { "newFile": "새 파일", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 0d5c3b41..b5201789 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -37,6 +37,7 @@ "title": "Configurações", "preferences": "Preferências", "nav": { + "general": "Geral", "appearance": "Aparência", "agents": "Agentes", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "Mensagens rápidas" } }, + "GeneralSettings": { + "loading": "Carregando...", + "sectionTitle": "Geral", + "sectionDescription": "Configure as preferências gerais do app.", + "loadFailed": "Falha ao carregar: {message}", + "openTargetTitle": "Abertura de arquivos", + "defaultOpenTarget": "Destino padrão de abertura", + "defaultOpenTargetDescription": "Local padrão para abrir arquivos e pastas", + "openTargetDesktopOnly": "Abrir arquivos em um editor nativo está disponível apenas no app desktop.", + "openTargetSaveFailed": "Falha ao salvar o destino de abertura: {message}", + "webFileOpenMethod": "Abertura de arquivos web", + "webFileOpenMethodDescription": "Método padrão para abrir HTML e outros arquivos web", + "webFileOpenMethodSaveFailed": "Falha ao salvar as configurações de abertura de arquivos web: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Gerenciador de arquivos", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Abrir no navegador", + "editor": "Usar destino padrão de abertura" + } + }, "AppearanceSettings": { "sectionTitle": "Aparência do tema", "sectionDescription": "Escolha claro, escuro ou seguir o sistema. As configurações são salvas automaticamente.", @@ -109,6 +133,14 @@ "proxyRequired": "A URL do proxy é obrigatória quando o proxy está ativado", "saveSuccess": "As configurações de proxy do sistema foram salvas", "saveFailed": "Falha ao salvar: {message}", + "openTargetTitle": "Abertura de arquivos", + "defaultOpenTarget": "Destino padrão de abertura", + "defaultOpenTargetDescription": "Local padrão para abrir arquivos e pastas", + "openTargetDesktopOnly": "Abrir arquivos em um editor nativo está disponível apenas no app desktop.", + "openTargetSaveFailed": "Falha ao salvar o destino de abertura: {message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "Idioma", "languageDescription": "Defina o idioma do app. Ao seguir o idioma do sistema, idiomas não suportados voltam para inglês.", "appLanguage": "Idioma do app", @@ -983,7 +1015,10 @@ "closeOthers": "Fechar outros", "closeAll": "Fechar tudo", "preview": "Visualizar", - "editSource": "Editar fonte" + "editSource": "Editar fonte", + "openInEditor": "Abrir no editor", + "openInEditorFailed": "Falha ao abrir no editor: {message}", + "openInBrowserFailed": "Falha ao abrir no navegador: {message}" }, "terminal": { "rename": "Renomear", @@ -1377,6 +1412,8 @@ "newFile": "Arquivo", "newDirectory": "Diretório", "openIn": "Abrir em", + "openInBrowser": "Abrir no navegador", + "openInEditor": "Abrir no editor", "openInTerminal": "Abrir no terminal", "actions": { "select": "Selecionar", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "Falha ao abrir diretório", + "openInEditorFailed": "Falha ao abrir no editor", "openBuiltinTerminalFailed": "Não foi possível abrir o terminal embutido", "openCommitWindowFailed": "Falha ao abrir janela de commit", "noAddableFilesInDir": "Nenhum arquivo alterado neste diretório pode ser adicionado ao VCS", @@ -1406,7 +1444,8 @@ "savedAsCopy": "Salvo como cópia", "saveCopyFailed": "Falha ao salvar como cópia", "watchStartFailed": "Falha ao iniciar monitoramento de arquivos", - "createFailed": "Falha ao criar" + "createFailed": "Falha ao criar", + "openInBrowserFailed": "Falha ao abrir no navegador" }, "createDialog": { "newFile": "Novo arquivo", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 8abe47e7..1998467d 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -37,6 +37,7 @@ "title": "设置", "preferences": "偏好设置", "nav": { + "general": "常规", "appearance": "外观", "agents": "智能体", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "快捷消息" } }, + "GeneralSettings": { + "loading": "加载中...", + "sectionTitle": "常规", + "sectionDescription": "配置应用的通用偏好设置。", + "loadFailed": "加载失败:{message}", + "openTargetTitle": "文件打开", + "defaultOpenTarget": "默认打开目标", + "defaultOpenTargetDescription": "打开文件和文件夹的默认位置", + "openTargetDesktopOnly": "在原生编辑器中打开文件仅桌面应用可用。", + "openTargetSaveFailed": "打开目标设置保存失败:{message}", + "webFileOpenMethod": "网页文件打开方式", + "webFileOpenMethodDescription": "html等文件默认打开方式", + "webFileOpenMethodSaveFailed": "网页文件打开方式保存失败:{message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "文件管理器", + "terminal": "终端" + }, + "webFileOpenMethods": { + "browser": "在浏览器中打开", + "editor": "使用默认打开目标" + } + }, "AppearanceSettings": { "sectionTitle": "主题外观", "sectionDescription": "选择浅色、深色或跟随系统主题,设置会自动保存。", @@ -109,6 +133,14 @@ "proxyRequired": "启用代理时必须填写代理地址", "saveSuccess": "系统代理设置已保存", "saveFailed": "保存失败:{message}", + "openTargetTitle": "文件打开", + "defaultOpenTarget": "默认打开目标", + "defaultOpenTargetDescription": "打开文件和文件夹的默认位置", + "openTargetDesktopOnly": "在原生编辑器中打开文件仅桌面应用可用。", + "openTargetSaveFailed": "打开目标设置保存失败:{message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "语言", "languageDescription": "设置应用语言。跟随系统时,若系统语言不受支持将回退为英文。", "appLanguage": "应用语言", @@ -983,7 +1015,10 @@ "closeOthers": "关闭其它", "closeAll": "关闭所有", "preview": "预览", - "editSource": "编辑源码" + "editSource": "编辑源码", + "openInEditor": "在编辑器中打开", + "openInEditorFailed": "在编辑器中打开失败:{message}", + "openInBrowserFailed": "在浏览器中打开失败:{message}" }, "terminal": { "rename": "重命名", @@ -1377,6 +1412,8 @@ "newFile": "文件", "newDirectory": "目录", "openIn": "打开于", + "openInBrowser": "在浏览器中打开", + "openInEditor": "在编辑器中打开", "openInTerminal": "在终端打开", "actions": { "select": "选择", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "打开目录失败", + "openInEditorFailed": "在编辑器中打开失败", "openBuiltinTerminalFailed": "无法打开内置终端", "openCommitWindowFailed": "打开提交窗口失败", "noAddableFilesInDir": "该目录下没有可添加到 VCS 的变更文件", @@ -1406,7 +1444,8 @@ "savedAsCopy": "已另存为副本", "saveCopyFailed": "另存为副本失败", "watchStartFailed": "文件监听启动失败", - "createFailed": "创建失败" + "createFailed": "创建失败", + "openInBrowserFailed": "在浏览器中打开失败" }, "createDialog": { "newFile": "新建文件", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 828b755e..478390b8 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -37,6 +37,7 @@ "title": "設定", "preferences": "偏好設定", "nav": { + "general": "常規", "appearance": "外觀", "agents": "智能體", "mcp": "MCP", @@ -51,6 +52,29 @@ "quick_messages": "快捷訊息" } }, + "GeneralSettings": { + "loading": "載入中...", + "sectionTitle": "常規", + "sectionDescription": "配置應用程式的一般偏好設定。", + "loadFailed": "載入失敗:{message}", + "openTargetTitle": "檔案開啟", + "defaultOpenTarget": "預設開啟目標", + "defaultOpenTargetDescription": "開啟檔案和資料夾的預設位置", + "openTargetDesktopOnly": "在原生編輯器中開啟檔案僅桌面應用程式可用。", + "openTargetSaveFailed": "開啟目標設定儲存失敗:{message}", + "webFileOpenMethod": "網頁檔案開啟方式", + "webFileOpenMethodDescription": "HTML 等檔案的預設開啟方式", + "webFileOpenMethodSaveFailed": "網頁檔案開啟方式儲存失敗:{message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "檔案管理器", + "terminal": "終端" + }, + "webFileOpenMethods": { + "browser": "在瀏覽器中開啟", + "editor": "使用預設開啟目標" + } + }, "AppearanceSettings": { "sectionTitle": "主題外觀", "sectionDescription": "選擇淺色、深色或跟隨系統主題,設定會自動儲存。", @@ -109,6 +133,14 @@ "proxyRequired": "啟用代理時必須填寫代理位址", "saveSuccess": "系統代理設定已儲存", "saveFailed": "儲存失敗:{message}", + "openTargetTitle": "檔案開啟", + "defaultOpenTarget": "預設開啟目標", + "defaultOpenTargetDescription": "開啟檔案和資料夾的預設位置", + "openTargetDesktopOnly": "在原生編輯器中開啟檔案僅桌面應用程式可用。", + "openTargetSaveFailed": "開啟目標設定儲存失敗:{message}", + "openTargets": { + "vscode": "VS Code" + }, "languageTitle": "語言", "languageDescription": "設定應用語言。跟隨系統時,若系統語言不受支援將回退為英文。", "appLanguage": "應用語言", @@ -983,7 +1015,10 @@ "closeOthers": "關閉其它", "closeAll": "關閉所有", "preview": "預覽", - "editSource": "編輯原始碼" + "editSource": "編輯原始碼", + "openInEditor": "在編輯器中開啟", + "openInEditorFailed": "在編輯器中開啟失敗:{message}", + "openInBrowserFailed": "在瀏覽器中開啟失敗:{message}" }, "terminal": { "rename": "重新命名", @@ -1377,6 +1412,8 @@ "newFile": "檔案", "newDirectory": "目錄", "openIn": "開啟於", + "openInBrowser": "在瀏覽器中開啟", + "openInEditor": "在編輯器中開啟", "openInTerminal": "在終端開啟", "actions": { "select": "選擇", @@ -1390,6 +1427,7 @@ }, "toasts": { "openDirectoryFailed": "開啟目錄失敗", + "openInEditorFailed": "在編輯器中開啟失敗", "openBuiltinTerminalFailed": "無法開啟內建終端", "openCommitWindowFailed": "打開提交視窗失敗", "noAddableFilesInDir": "該目錄下沒有可加入到 VCS 的變更檔案", @@ -1406,7 +1444,8 @@ "savedAsCopy": "已另存為副本", "saveCopyFailed": "另存為副本失敗", "watchStartFailed": "檔案監聽啟動失敗", - "createFailed": "建立失敗" + "createFailed": "建立失敗", + "openInBrowserFailed": "在瀏覽器中開啟失敗" }, "createDialog": { "newFile": "新建檔案", diff --git a/src/lib/api.ts b/src/lib/api.ts index ec91105b..a8de5cab 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -46,6 +46,8 @@ import type { WorkspaceSnapshotResponse, GitLogResult, SystemLanguageSettings, + SystemOpenTarget, + SystemOpenTargetSettings, SystemProxySettings, SystemRenderingSettings, GitCredentials, @@ -459,6 +461,28 @@ export async function updateSystemLanguageSettings( return getTransport().call("update_system_language_settings", { settings }) } +export async function getSystemOpenTargetSettings(): Promise { + return getTransport().call("get_system_open_target_settings") +} + +export async function updateSystemOpenTargetSettings( + settings: SystemOpenTargetSettings +): Promise { + return getTransport().call("update_system_open_target_settings", { settings }) +} + +export async function openPathWithTarget(params: { + folderPath: string + relativePath: string + target?: SystemOpenTarget | null +}): Promise { + return getTransport().call("open_path_with_target", { + folderPath: params.folderPath, + relativePath: params.relativePath, + target: params.target ?? null, + }) +} + export async function getSystemRenderingSettings(): Promise { return getTransport().call("get_system_rendering_settings") } @@ -1104,6 +1128,7 @@ export async function openCommitWindow(folderId: number): Promise { } export type SettingsSection = + | "general" | "appearance" | "agents" | "mcp" diff --git a/src/lib/open-targets.ts b/src/lib/open-targets.ts new file mode 100644 index 00000000..8f189101 --- /dev/null +++ b/src/lib/open-targets.ts @@ -0,0 +1,114 @@ +import { isDesktop } from "@/lib/platform" +import type { SystemOpenTarget } from "@/lib/types" + +export type OpenTargetKind = "editor" | "file_manager" | "terminal" + +export type OpenTargetSettingsLabelKey = + | "openTargets.vscode" + | "openTargets.file_manager" + | "openTargets.terminal" + +export type FileTreeOpenTargetLabelKey = + | "openInBrowser" + | "openInEditor" + | "openInFileManager" + | "openInFinder" + | "openInExplorer" + | "openInTerminal" + +export interface OpenTargetRegistryItem { + id: SystemOpenTarget + kind: OpenTargetKind + labelKey: OpenTargetSettingsLabelKey + fileTreeLabelKey: FileTreeOpenTargetLabelKey + supportsFiles: boolean + supportsDirectories: boolean + desktopOnly: boolean +} + +export const OPEN_TARGET_REGISTRY: OpenTargetRegistryItem[] = [ + { + id: "vscode", + kind: "editor", + labelKey: "openTargets.vscode", + fileTreeLabelKey: "openInEditor", + supportsFiles: true, + supportsDirectories: false, + desktopOnly: true, + }, + { + id: "file_manager", + kind: "file_manager", + labelKey: "openTargets.file_manager", + fileTreeLabelKey: "openInFileManager", + supportsFiles: true, + supportsDirectories: true, + desktopOnly: true, + }, + { + id: "terminal", + kind: "terminal", + labelKey: "openTargets.terminal", + fileTreeLabelKey: "openInTerminal", + supportsFiles: true, + supportsDirectories: true, + desktopOnly: false, + }, +] + +export const OPEN_TARGET_VALUES: SystemOpenTarget[] = OPEN_TARGET_REGISTRY.map( + (target) => target.id +) + +export function isSystemOpenTarget(value: string): value is SystemOpenTarget { + return OPEN_TARGET_VALUES.includes(value as SystemOpenTarget) +} + +export function isWebFilePath(path: string): boolean { + const cleanPath = path.split(/[?#]/, 1)[0] ?? path + const extension = cleanPath.split(".").pop()?.toLowerCase() + return extension === "html" || extension === "htm" +} + +export function isWebFileLanguage(language?: string | null): boolean { + return language === "html" +} + +export function getOpenTargetRegistryItems(): OpenTargetRegistryItem[] { + return OPEN_TARGET_REGISTRY.filter( + (target) => !target.desktopOnly || isDesktop() + ) +} + +export function getOpenTargetRegistryItem( + target: SystemOpenTarget +): OpenTargetRegistryItem | null { + return OPEN_TARGET_REGISTRY.find((item) => item.id === target) ?? null +} + +export function getFileTreeOpenTargetItems( + nodeKind: "file" | "dir" +): OpenTargetRegistryItem[] { + return getOpenTargetRegistryItems().filter((target) => + nodeKind === "file" ? target.supportsFiles : target.supportsDirectories + ) +} + +export function getPlatformFileManagerLabelKey(): + | "openInFileManager" + | "openInFinder" + | "openInExplorer" { + if (typeof navigator === "undefined") return "openInFileManager" + + const platform = `${navigator.platform} ${navigator.userAgent}`.toLowerCase() + if (platform.includes("mac")) return "openInFinder" + if (platform.includes("win")) return "openInExplorer" + return "openInFileManager" +} + +export function getFileTreeOpenTargetLabelKey( + target: OpenTargetRegistryItem +): FileTreeOpenTargetLabelKey { + if (target.id === "file_manager") return getPlatformFileManagerLabelKey() + return target.fileTreeLabelKey +} diff --git a/src/lib/platform.ts b/src/lib/platform.ts index 4d73a594..5ec68a63 100644 --- a/src/lib/platform.ts +++ b/src/lib/platform.ts @@ -1,4 +1,5 @@ -import { isDesktop, getTransport } from "./transport" +import { detectEnvironment } from "./transport/detect" +import { getTransport } from "./transport" import type { UnsubscribeFn } from "./transport" /** @@ -6,7 +7,9 @@ import type { UnsubscribeFn } from "./transport" * Tauri desktop and web browser environments. */ -export { isDesktop } +export function isDesktop(): boolean { + return detectEnvironment() === "tauri" +} /** * Subscribe to backend events. diff --git a/src/lib/tauri.ts b/src/lib/tauri.ts index 9053cbe7..aa536022 100644 --- a/src/lib/tauri.ts +++ b/src/lib/tauri.ts @@ -881,6 +881,7 @@ export async function openCommitWindow(folderId: number): Promise { } export type SettingsSection = + | "general" | "appearance" | "agents" | "mcp" diff --git a/src/lib/transport/index.ts b/src/lib/transport/index.ts index af135e5f..e1974e46 100644 --- a/src/lib/transport/index.ts +++ b/src/lib/transport/index.ts @@ -28,5 +28,5 @@ export function getTransport(): Transport { } export function isDesktop(): boolean { - return getTransport().isDesktop() + return detectEnvironment() === "tauri" } diff --git a/src/lib/types.ts b/src/lib/types.ts index 369832c5..150472cd 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -634,6 +634,15 @@ export interface SystemLanguageSettings { language: AppLocale } +export type SystemOpenTarget = "vscode" | "file_manager" | "terminal" + +export type SystemWebFileOpenMethod = "browser" | "editor" + +export interface SystemOpenTargetSettings { + target: SystemOpenTarget + web_file_open_method: SystemWebFileOpenMethod +} + export interface SystemRenderingSettings { disable_hardware_acceleration: boolean } From c96756c19029153deb185fd188d8d41aafdc8fbf Mon Sep 17 00:00:00 2001 From: lee Date: Sun, 26 Apr 2026 20:48:41 +0800 Subject: [PATCH 2/4] fix(settings): place general nav below appearance [T-04-26-move-general-settings-nav-below-appearance] --- src/components/settings/settings-shell.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index c85425b7..de076489 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -54,16 +54,16 @@ interface SettingsNavItem { } const SETTINGS_NAV_ITEMS: SettingsNavItem[] = [ - { - href: "/settings/general", - labelKey: "general", - icon: SlidersHorizontal, - }, { href: "/settings/appearance", labelKey: "appearance", icon: Palette, }, + { + href: "/settings/general", + labelKey: "general", + icon: SlidersHorizontal, + }, { href: "/settings/mcp", labelKey: "mcp", From b35aff3424a8c5e8263adc2805e1534298a57607 Mon Sep 17 00:00:00 2001 From: lee Date: Fri, 1 May 2026 22:17:52 +0800 Subject: [PATCH 3/4] fix(settings): clean up open target review feedback Align the settings landing flow with the General page, remove dead open-target i18n keys, and simplify open-target persistence logic without changing the existing path safety checks. --- src-tauri/src/commands/system_settings.rs | 26 +++++++--------------- src/components/settings/settings-shell.tsx | 10 ++++----- src/i18n/messages/ar.json | 8 ------- src/i18n/messages/de.json | 8 ------- src/i18n/messages/en.json | 8 ------- src/i18n/messages/es.json | 8 ------- src/i18n/messages/fr.json | 8 ------- src/i18n/messages/ja.json | 8 ------- src/i18n/messages/ko.json | 8 ------- src/i18n/messages/pt.json | 8 ------- src/i18n/messages/zh-CN.json | 8 ------- src/i18n/messages/zh-TW.json | 8 ------- 12 files changed, 13 insertions(+), 103 deletions(-) diff --git a/src-tauri/src/commands/system_settings.rs b/src-tauri/src/commands/system_settings.rs index a6058447..14016c5e 100644 --- a/src-tauri/src/commands/system_settings.rs +++ b/src-tauri/src/commands/system_settings.rs @@ -59,10 +59,6 @@ fn normalize_proxy_settings( }) } -fn normalize_open_target_settings(settings: SystemOpenTargetSettings) -> SystemOpenTargetSettings { - settings -} - #[cfg(feature = "tauri-runtime")] struct ResolvedWorkspacePath { root: PathBuf, @@ -278,19 +274,17 @@ pub(crate) async fn load_system_open_target_settings( return Ok(SystemOpenTargetSettings::default()); }; - let parsed = serde_json::from_str::(&raw).map_err(|e| { + serde_json::from_str::(&raw).map_err(|e| { AppCommandError::configuration_invalid("Failed to parse stored open target settings") .with_detail(e.to_string()) - })?; - Ok(normalize_open_target_settings(parsed)) + }) } pub(crate) async fn update_system_open_target_settings_core( conn: &DatabaseConnection, settings: SystemOpenTargetSettings, ) -> Result { - let normalized = normalize_open_target_settings(settings); - let serialized = serde_json::to_string(&normalized).map_err(|e| { + let serialized = serde_json::to_string(&settings).map_err(|e| { AppCommandError::invalid_input("Failed to serialize open target settings") .with_detail(e.to_string()) })?; @@ -299,7 +293,7 @@ pub(crate) async fn update_system_open_target_settings_core( .await .map_err(AppCommandError::from)?; - Ok(normalized) + Ok(settings) } #[cfg(feature = "tauri-runtime")] @@ -309,16 +303,12 @@ pub(crate) async fn open_path_with_target_core( target: Option, conn: &DatabaseConnection, ) -> Result<(), AppCommandError> { - let settings = match target { - Some(target) => SystemOpenTargetSettings { - target, - ..SystemOpenTargetSettings::default() - }, - None => load_system_open_target_settings(conn).await?, + let target = match target { + Some(target) => target, + None => load_system_open_target_settings(conn).await?.target, }; - let normalized = normalize_open_target_settings(settings); - match normalized.target { + match target { SystemOpenTarget::Vscode => { let resolved_path = resolve_workspace_relative_path(&folder_path, &relative_path)?; open_path_in_vscode(&resolved_path.root, &resolved_path.target)?; diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index de076489..c85425b7 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -54,16 +54,16 @@ interface SettingsNavItem { } const SETTINGS_NAV_ITEMS: SettingsNavItem[] = [ - { - href: "/settings/appearance", - labelKey: "appearance", - icon: Palette, - }, { href: "/settings/general", labelKey: "general", icon: SlidersHorizontal, }, + { + href: "/settings/appearance", + labelKey: "appearance", + icon: Palette, + }, { href: "/settings/mcp", labelKey: "mcp", diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 4e05aada..a1dd6fea 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -133,14 +133,6 @@ "proxyRequired": "عنوان URL للوكيل مطلوب عند تفعيل الوكيل", "saveSuccess": "تم حفظ إعدادات وكيل النظام", "saveFailed": "فشل الحفظ: {message}", - "openTargetTitle": "فتح الملفات", - "defaultOpenTarget": "هدف الفتح الافتراضي", - "defaultOpenTargetDescription": "الموقع الافتراضي لفتح الملفات والمجلدات", - "openTargetDesktopOnly": "فتح الملفات في محرر أصلي متاح فقط في تطبيق سطح المكتب.", - "openTargetSaveFailed": "فشل حفظ هدف الفتح: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "اللغة", "languageDescription": "حدد لغة التطبيق. عند اتباع لغة النظام، ستعود اللغات غير المدعومة إلى الإنجليزية.", "appLanguage": "لغة التطبيق", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 52ab4f9d..862d4ffc 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -133,14 +133,6 @@ "proxyRequired": "Bei aktiviertem Proxy ist eine Proxy-URL erforderlich", "saveSuccess": "System-Proxy-Einstellungen wurden gespeichert", "saveFailed": "Speichern fehlgeschlagen: {message}", - "openTargetTitle": "Dateien öffnen", - "defaultOpenTarget": "Standardziel zum Öffnen", - "defaultOpenTargetDescription": "Standardort zum Öffnen von Dateien und Ordnern", - "openTargetDesktopOnly": "Das Öffnen in einem nativen Editor ist nur in der Desktop-App verfügbar.", - "openTargetSaveFailed": "Öffnungsziel konnte nicht gespeichert werden: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "Sprache", "languageDescription": "Lege die App-Sprache fest. Bei Systemsprache wird bei nicht unterstützten Sprachen auf Englisch zurückgefallen.", "appLanguage": "App-Sprache", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index ca5f942e..e9a95c92 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -133,14 +133,6 @@ "proxyRequired": "Proxy URL is required when proxy is enabled", "saveSuccess": "System proxy settings saved", "saveFailed": "Save failed: {message}", - "openTargetTitle": "File opening", - "defaultOpenTarget": "Default open target", - "defaultOpenTargetDescription": "Default location for opening files and folders", - "openTargetDesktopOnly": "Opening files in a native editor is available in the desktop app.", - "openTargetSaveFailed": "Failed to save open target settings: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "Language", "languageDescription": "Set app language. When following system locale, unsupported languages fall back to English.", "appLanguage": "App language", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 951b2eec..bba2d0be 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -133,14 +133,6 @@ "proxyRequired": "Se requiere URL del proxy cuando el proxy está activado", "saveSuccess": "La configuración del proxy del sistema se guardó", "saveFailed": "Error al guardar: {message}", - "openTargetTitle": "Apertura de archivos", - "defaultOpenTarget": "Destino predeterminado de apertura", - "defaultOpenTargetDescription": "Ubicación predeterminada para abrir archivos y carpetas", - "openTargetDesktopOnly": "Abrir archivos en un editor nativo solo está disponible en la app de escritorio.", - "openTargetSaveFailed": "No se pudo guardar el destino de apertura: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "Idioma", "languageDescription": "Configura el idioma de la app. Al seguir el idioma del sistema, los no compatibles vuelven a inglés.", "appLanguage": "Idioma de la app", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 6d210cd3..5a63fbd2 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -133,14 +133,6 @@ "proxyRequired": "L’URL du proxy est requise lorsque le proxy est activé", "saveSuccess": "Les paramètres du proxy système ont été enregistrés", "saveFailed": "Échec de l’enregistrement : {message}", - "openTargetTitle": "Ouverture de fichiers", - "defaultOpenTarget": "Cible d’ouverture par défaut", - "defaultOpenTargetDescription": "Emplacement par défaut pour ouvrir les fichiers et dossiers", - "openTargetDesktopOnly": "L’ouverture dans un éditeur natif est disponible uniquement dans l’app de bureau.", - "openTargetSaveFailed": "Échec de l’enregistrement de la cible d’ouverture : {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "Langue", "languageDescription": "Définissez la langue de l’app. En mode système, les langues non prises en charge reviennent à l’anglais.", "appLanguage": "Langue de l’app", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index 8de05eb0..04ec45f5 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -133,14 +133,6 @@ "proxyRequired": "プロキシ有効時はプロキシ URL が必要です", "saveSuccess": "システムプロキシ設定を保存しました", "saveFailed": "保存に失敗しました: {message}", - "openTargetTitle": "ファイルを開く", - "defaultOpenTarget": "デフォルトの開き先", - "defaultOpenTargetDescription": "ファイルとフォルダーを開くデフォルトの場所", - "openTargetDesktopOnly": "ネイティブエディターでファイルを開く機能はデスクトップアプリでのみ利用できます。", - "openTargetSaveFailed": "開き先設定の保存に失敗しました: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "言語", "languageDescription": "アプリの言語を設定します。システムに従う場合、未対応言語は英語にフォールバックします。", "appLanguage": "アプリ言語", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 144a9d6e..c7846222 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -133,14 +133,6 @@ "proxyRequired": "프록시를 활성화하면 프록시 URL이 필요합니다", "saveSuccess": "시스템 프록시 설정이 저장되었습니다", "saveFailed": "저장 실패: {message}", - "openTargetTitle": "파일 열기", - "defaultOpenTarget": "기본 열기 대상", - "defaultOpenTargetDescription": "파일과 폴더를 열 기본 위치", - "openTargetDesktopOnly": "네이티브 편집기에서 파일 열기는 데스크톱 앱에서만 사용할 수 있습니다.", - "openTargetSaveFailed": "열기 대상 설정 저장 실패: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "언어", "languageDescription": "앱 언어를 설정합니다. 시스템 언어를 따를 때 지원되지 않는 언어는 영어로 대체됩니다.", "appLanguage": "앱 언어", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index b5201789..628ddd31 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -133,14 +133,6 @@ "proxyRequired": "A URL do proxy é obrigatória quando o proxy está ativado", "saveSuccess": "As configurações de proxy do sistema foram salvas", "saveFailed": "Falha ao salvar: {message}", - "openTargetTitle": "Abertura de arquivos", - "defaultOpenTarget": "Destino padrão de abertura", - "defaultOpenTargetDescription": "Local padrão para abrir arquivos e pastas", - "openTargetDesktopOnly": "Abrir arquivos em um editor nativo está disponível apenas no app desktop.", - "openTargetSaveFailed": "Falha ao salvar o destino de abertura: {message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "Idioma", "languageDescription": "Defina o idioma do app. Ao seguir o idioma do sistema, idiomas não suportados voltam para inglês.", "appLanguage": "Idioma do app", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 1998467d..fc5c0a34 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -133,14 +133,6 @@ "proxyRequired": "启用代理时必须填写代理地址", "saveSuccess": "系统代理设置已保存", "saveFailed": "保存失败:{message}", - "openTargetTitle": "文件打开", - "defaultOpenTarget": "默认打开目标", - "defaultOpenTargetDescription": "打开文件和文件夹的默认位置", - "openTargetDesktopOnly": "在原生编辑器中打开文件仅桌面应用可用。", - "openTargetSaveFailed": "打开目标设置保存失败:{message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "语言", "languageDescription": "设置应用语言。跟随系统时,若系统语言不受支持将回退为英文。", "appLanguage": "应用语言", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 478390b8..14540ee1 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -133,14 +133,6 @@ "proxyRequired": "啟用代理時必須填寫代理位址", "saveSuccess": "系統代理設定已儲存", "saveFailed": "儲存失敗:{message}", - "openTargetTitle": "檔案開啟", - "defaultOpenTarget": "預設開啟目標", - "defaultOpenTargetDescription": "開啟檔案和資料夾的預設位置", - "openTargetDesktopOnly": "在原生編輯器中開啟檔案僅桌面應用程式可用。", - "openTargetSaveFailed": "開啟目標設定儲存失敗:{message}", - "openTargets": { - "vscode": "VS Code" - }, "languageTitle": "語言", "languageDescription": "設定應用語言。跟隨系統時,若系統語言不受支援將回退為英文。", "appLanguage": "應用語言", From f523c3109dd4c8c71ecb5ed96b49e8976cdcde06 Mon Sep 17 00:00:00 2001 From: lee Date: Sat, 2 May 2026 11:30:38 +0800 Subject: [PATCH 4/4] refactor(settings): move open target controls into system Move file-opening preferences into System settings, remove the top-level General entry, and retarget settings fallbacks to System so the remaining navigation and routes stay consistent. --- src-tauri/src/commands/windows.rs | 3 +- src-tauri/src/web/handlers/folders.rs | 3 +- src/app/settings/general/page.tsx | 5 - src/app/settings/page.tsx | 2 +- src/components/settings/general-settings.tsx | 209 ---------------- src/components/settings/settings-shell.tsx | 7 - .../settings/system-network-settings.tsx | 225 ++++++++++++++++-- src/i18n/messages/ar.json | 41 ++-- src/i18n/messages/de.json | 41 ++-- src/i18n/messages/en.json | 41 ++-- src/i18n/messages/es.json | 41 ++-- src/i18n/messages/fr.json | 41 ++-- src/i18n/messages/ja.json | 41 ++-- src/i18n/messages/ko.json | 41 ++-- src/i18n/messages/pt.json | 41 ++-- src/i18n/messages/zh-CN.json | 41 ++-- src/i18n/messages/zh-TW.json | 41 ++-- src/lib/api.ts | 3 +- src/lib/tauri.ts | 1 - 19 files changed, 374 insertions(+), 494 deletions(-) delete mode 100644 src/app/settings/general/page.tsx delete mode 100644 src/components/settings/general-settings.tsx diff --git a/src-tauri/src/commands/windows.rs b/src-tauri/src/commands/windows.rs index 68a14a30..76fee6ce 100644 --- a/src-tauri/src/commands/windows.rs +++ b/src-tauri/src/commands/windows.rs @@ -301,14 +301,13 @@ impl Default for CommitWindowState { fn resolve_settings_route(section: Option<&str>) -> &'static str { match section { - Some("general") => "settings/general", Some("appearance") => "settings/appearance", Some("agents") => "settings/agents", Some("mcp") => "settings/mcp", Some("skills") => "settings/skills", Some("shortcuts") => "settings/shortcuts", Some("system") => "settings/system", - _ => "settings/general", + _ => "settings/system", } } diff --git a/src-tauri/src/web/handlers/folders.rs b/src-tauri/src/web/handlers/folders.rs index d1c99780..7b49c791 100644 --- a/src-tauri/src/web/handlers/folders.rs +++ b/src-tauri/src/web/handlers/folders.rs @@ -223,14 +223,13 @@ pub async fn open_settings_window( Json(params): Json, ) -> Result, AppCommandError> { let route = match params.section.as_deref() { - Some("general") => "settings/general", Some("appearance") => "settings/appearance", Some("agents") => "settings/agents", Some("mcp") => "settings/mcp", Some("skills") => "settings/skills", Some("shortcuts") => "settings/shortcuts", Some("system") => "settings/system", - _ => "settings/general", + _ => "settings/system", }; let path = if route == "settings/agents" { diff --git a/src/app/settings/general/page.tsx b/src/app/settings/general/page.tsx deleted file mode 100644 index fcac692a..00000000 --- a/src/app/settings/general/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { GeneralSettings } from "@/components/settings/general-settings" - -export default function SettingsGeneralPage() { - return -} diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index ae963268..36588a82 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -7,7 +7,7 @@ export default function SettingsPage() { const router = useRouter() useEffect(() => { - router.replace("/settings/general") + router.replace("/settings/system") }, [router]) return null diff --git a/src/components/settings/general-settings.tsx b/src/components/settings/general-settings.tsx deleted file mode 100644 index ca5a279e..00000000 --- a/src/components/settings/general-settings.tsx +++ /dev/null @@ -1,209 +0,0 @@ -"use client" - -import { useCallback, useEffect, useState } from "react" -import { ExternalLink, Loader2 } from "lucide-react" -import { useTranslations } from "next-intl" -import { toast } from "sonner" -import { ScrollArea } from "@/components/ui/scroll-area" -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select" -import { - getSystemOpenTargetSettings, - updateSystemOpenTargetSettings, -} from "@/lib/api" -import { toErrorMessage } from "@/lib/app-error" -import { OPEN_TARGET_REGISTRY, isSystemOpenTarget } from "@/lib/open-targets" -import { isDesktop } from "@/lib/platform" -import type { SystemOpenTarget, SystemWebFileOpenMethod } from "@/lib/types" - -function isSystemWebFileOpenMethod( - value: string -): value is SystemWebFileOpenMethod { - return value === "browser" || value === "editor" -} - -export function GeneralSettings() { - const t = useTranslations("GeneralSettings") - const [loading, setLoading] = useState(true) - const [savingOpenTarget, setSavingOpenTarget] = useState(false) - const [savingWebFileOpenMethod, setSavingWebFileOpenMethod] = useState(false) - const [openTarget, setOpenTarget] = useState("file_manager") - const [webFileOpenMethod, setWebFileOpenMethod] = - useState("browser") - const [loadError, setLoadError] = useState(null) - const savingOpenSettings = savingOpenTarget || savingWebFileOpenMethod - - const loadSettings = useCallback(async () => { - setLoading(true) - setLoadError(null) - - try { - const settings = await getSystemOpenTargetSettings() - setOpenTarget(settings.target) - setWebFileOpenMethod(settings.web_file_open_method ?? "browser") - } catch (err) { - const message = toErrorMessage(err) - setLoadError(message) - } finally { - setLoading(false) - } - }, []) - - useEffect(() => { - void loadSettings() - }, [loadSettings]) - - const saveOpenTarget = useCallback( - async (target: SystemOpenTarget, previous: SystemOpenTarget) => { - setSavingOpenTarget(true) - - try { - const next = await updateSystemOpenTargetSettings({ - target, - web_file_open_method: webFileOpenMethod, - }) - setOpenTarget(next.target) - setWebFileOpenMethod(next.web_file_open_method) - } catch (err) { - setOpenTarget(previous) - const message = toErrorMessage(err) - toast.error(t("openTargetSaveFailed", { message })) - } finally { - setSavingOpenTarget(false) - } - }, - [t, webFileOpenMethod] - ) - - const saveWebFileOpenMethod = useCallback( - async ( - method: SystemWebFileOpenMethod, - previous: SystemWebFileOpenMethod - ) => { - setSavingWebFileOpenMethod(true) - - try { - const next = await updateSystemOpenTargetSettings({ - target: openTarget, - web_file_open_method: method, - }) - setOpenTarget(next.target) - setWebFileOpenMethod(next.web_file_open_method) - } catch (err) { - setWebFileOpenMethod(previous) - const message = toErrorMessage(err) - toast.error(t("webFileOpenMethodSaveFailed", { message })) - } finally { - setSavingWebFileOpenMethod(false) - } - }, - [openTarget, t] - ) - - if (loading) { - return ( -
- - {t("loading")} -
- ) - } - - return ( - -
-
-

{t("sectionTitle")}

-

- {t("sectionDescription")} -

-
- -
-
- -

{t("openTargetTitle")}

-
- - {loadError && ( -
- {t("loadFailed", { message: loadError })} -
- )} - -
-
-

{t("defaultOpenTarget")}

-

- {t("defaultOpenTargetDescription")} -

-
- -
- -
-
-

{t("webFileOpenMethod")}

-

- {t("webFileOpenMethodDescription")} -

-
- -
- - {!isDesktop() && ( -

- {t("openTargetDesktopOnly")} -

- )} -
-
-
- ) -} diff --git a/src/components/settings/settings-shell.tsx b/src/components/settings/settings-shell.tsx index c85425b7..3321619b 100644 --- a/src/components/settings/settings-shell.tsx +++ b/src/components/settings/settings-shell.tsx @@ -20,7 +20,6 @@ import { PlugZap, Server, Settings, - SlidersHorizontal, Sparkles, } from "lucide-react" import { useTranslations } from "next-intl" @@ -37,7 +36,6 @@ import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet" interface SettingsNavItem { href: string labelKey: - | "general" | "appearance" | "agents" | "model_providers" @@ -54,11 +52,6 @@ interface SettingsNavItem { } const SETTINGS_NAV_ITEMS: SettingsNavItem[] = [ - { - href: "/settings/general", - labelKey: "general", - icon: SlidersHorizontal, - }, { href: "/settings/appearance", labelKey: "appearance", diff --git a/src/components/settings/system-network-settings.tsx b/src/components/settings/system-network-settings.tsx index 2eca233f..2c3ae367 100644 --- a/src/components/settings/system-network-settings.tsx +++ b/src/components/settings/system-network-settings.tsx @@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from "react" import { ArrowUpCircle, CheckCircle2, + ExternalLink, Languages, Loader2, MonitorCog, @@ -29,14 +30,22 @@ import { SelectValue, } from "@/components/ui/select" import { + getSystemOpenTargetSettings, getSystemProxySettings, getSystemRenderingSettings, updateSystemLanguageSettings, + updateSystemOpenTargetSettings, updateSystemProxySettings, updateSystemRenderingSettings, } from "@/lib/api" +import { OPEN_TARGET_REGISTRY, isSystemOpenTarget } from "@/lib/open-targets" import { isDesktop, openUrl } from "@/lib/platform" -import type { AppLocale } from "@/lib/types" +import { toErrorMessage } from "@/lib/app-error" +import type { + AppLocale, + SystemOpenTarget, + SystemWebFileOpenMethod, +} from "@/lib/types" import { usePlatform } from "@/hooks/use-platform" import { checkAppUpdate, @@ -64,6 +73,12 @@ function isAppLocale(value: string): value is AppLocale { return APP_LANGUAGE_VALUES.includes(value as AppLocale) } +function isSystemWebFileOpenMethod( + value: string +): value is SystemWebFileOpenMethod { + return value === "browser" || value === "editor" +} + type UpdateAction = "check" | "install" // Captured the first time settings page loads: represents the value that the @@ -87,8 +102,16 @@ export function SystemNetworkSettings() { const [savingLanguage, setSavingLanguage] = useState(false) const [enabled, setEnabled] = useState(false) const [proxyUrl, setProxyUrl] = useState("") + const [openTarget, setOpenTarget] = useState("file_manager") + const [webFileOpenMethod, setWebFileOpenMethod] = + useState("browser") + const [openTargetLoadError, setOpenTargetLoadError] = useState( + null + ) const [loadError, setLoadError] = useState(null) const [disableHwAccel, setDisableHwAccel] = useState(false) + const [savingOpenTarget, setSavingOpenTarget] = useState(false) + const [savingWebFileOpenMethod, setSavingWebFileOpenMethod] = useState(false) const [savingRendering, setSavingRendering] = useState(false) const [persistedDisableHwAccel, setPersistedDisableHwAccel] = useState(false) const [processStartLoaded, setProcessStartLoaded] = useState( @@ -96,6 +119,7 @@ export function SystemNetworkSettings() { ) const renderingDirty = processStartLoaded && persistedDisableHwAccel !== processStartDisableHwAccel + const savingOpenSettings = savingOpenTarget || savingWebFileOpenMethod const [currentVersion, setCurrentVersion] = useState("") const [availableUpdate, setAvailableUpdate] = useState(null) const [checkingUpdate, setCheckingUpdate] = useState(false) @@ -168,33 +192,57 @@ export function SystemNetworkSettings() { const loadSettings = useCallback(async () => { setLoading(true) + setOpenTargetLoadError(null) setLoadError(null) + const tasks: Promise[] = [ + getSystemOpenTargetSettings() + .then((settings) => { + setOpenTarget(settings.target) + setWebFileOpenMethod(settings.web_file_open_method ?? "browser") + }) + .catch((err) => { + setOpenTargetLoadError(toErrorMessage(err)) + console.error("[Settings] load open target settings failed:", err) + }), + getSystemProxySettings() + .then((settings) => { + setEnabled(settings.enabled) + setProxyUrl(settings.proxy_url ?? "") + }) + .catch((err) => { + setLoadError(toErrorMessage(err)) + console.error("[Settings] load proxy settings failed:", err) + }), + getCurrentAppVersion() + .then((version) => { + setCurrentVersion(version) + }) + .catch((err) => { + console.error("[Settings] load app version failed:", err) + }), + ] + + if (renderingSettingsLoadable) { + tasks.push( + getSystemRenderingSettings() + .then((settings) => { + const value = settings.disable_hardware_acceleration + setDisableHwAccel(value) + setPersistedDisableHwAccel(value) + if (processStartDisableHwAccel === null) { + processStartDisableHwAccel = value + setProcessStartLoaded(true) + } + }) + .catch((err) => { + console.error("[Settings] load rendering settings failed:", err) + }) + ) + } + try { - const [proxySettings, version, renderingSettings] = await Promise.all([ - getSystemProxySettings(), - getCurrentAppVersion(), - renderingSettingsLoadable - ? getSystemRenderingSettings() - : Promise.resolve(null), - ]) - - setEnabled(proxySettings.enabled) - setProxyUrl(proxySettings.proxy_url ?? "") - setCurrentVersion(version) - if (renderingSettings) { - const value = renderingSettings.disable_hardware_acceleration - setDisableHwAccel(value) - setPersistedDisableHwAccel(value) - if (processStartDisableHwAccel === null) { - processStartDisableHwAccel = value - setProcessStartLoaded(true) - } - } - } catch (err) { - const message = err instanceof Error ? err.message : String(err) - setLoadError(message) - console.error("[Settings] load system settings failed:", err) + await Promise.all(tasks) } finally { setLoading(false) } @@ -219,6 +267,53 @@ export function SystemNetworkSettings() { } }, [availableUpdate]) + const saveOpenTarget = useCallback( + async (target: SystemOpenTarget, previous: SystemOpenTarget) => { + setSavingOpenTarget(true) + + try { + const next = await updateSystemOpenTargetSettings({ + target, + web_file_open_method: webFileOpenMethod, + }) + setOpenTarget(next.target) + setWebFileOpenMethod(next.web_file_open_method) + } catch (err) { + setOpenTarget(previous) + const message = toErrorMessage(err) + toast.error(t("openTargetSaveFailed", { message })) + } finally { + setSavingOpenTarget(false) + } + }, + [t, webFileOpenMethod] + ) + + const saveWebFileOpenMethod = useCallback( + async ( + method: SystemWebFileOpenMethod, + previous: SystemWebFileOpenMethod + ) => { + setSavingWebFileOpenMethod(true) + + try { + const next = await updateSystemOpenTargetSettings({ + target: openTarget, + web_file_open_method: method, + }) + setOpenTarget(next.target) + setWebFileOpenMethod(next.web_file_open_method) + } catch (err) { + setWebFileOpenMethod(previous) + const message = toErrorMessage(err) + toast.error(t("webFileOpenMethodSaveFailed", { message })) + } finally { + setSavingWebFileOpenMethod(false) + } + }, + [openTarget, t] + ) + const saveProxySettings = useCallback( async (nextEnabled: boolean, nextProxyUrl: string) => { if (nextEnabled && !nextProxyUrl.trim()) return @@ -574,6 +669,86 @@ export function SystemNetworkSettings() { )} +
+
+ +

{t("openTargetTitle")}

+
+ + {openTargetLoadError && ( +
+ {t("loadFailed", { message: openTargetLoadError })} +
+ )} + +
+
+

{t("defaultOpenTarget")}

+

+ {t("defaultOpenTargetDescription")} +

+
+ +
+ +
+
+

{t("webFileOpenMethod")}

+

+ {t("webFileOpenMethodDescription")} +

+
+ +
+ + {!isDesktop() && ( +

+ {t("openTargetDesktopOnly")} +

+ )} +
+
diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index a1dd6fea..39d838c3 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -37,7 +37,6 @@ "title": "الإعدادات", "preferences": "التفضيلات", "nav": { - "general": "عام", "appearance": "المظهر", "agents": "الوكلاء", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "رسائل سريعة" } }, - "GeneralSettings": { - "loading": "جارٍ التحميل...", - "sectionTitle": "عام", - "sectionDescription": "اضبط التفضيلات العامة للتطبيق.", - "loadFailed": "فشل التحميل: {message}", - "openTargetTitle": "فتح الملفات", - "defaultOpenTarget": "هدف الفتح الافتراضي", - "defaultOpenTargetDescription": "الموقع الافتراضي لفتح الملفات والمجلدات", - "openTargetDesktopOnly": "فتح الملفات في محرر أصلي متاح فقط في تطبيق سطح المكتب.", - "openTargetSaveFailed": "فشل حفظ هدف الفتح: {message}", - "webFileOpenMethod": "طريقة فتح ملفات الويب", - "webFileOpenMethodDescription": "الطريقة الافتراضية لفتح ملفات HTML وملفات الويب الأخرى", - "webFileOpenMethodSaveFailed": "فشل حفظ إعدادات فتح ملفات الويب: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "مدير الملفات", - "terminal": "الطرفية" - }, - "webFileOpenMethods": { - "browser": "فتح في المتصفح", - "editor": "استخدام هدف الفتح الافتراضي" - } - }, "AppearanceSettings": { "sectionTitle": "مظهر السمة", "sectionDescription": "اختر الفاتح أو الداكن أو اتباع النظام. يتم حفظ الإعدادات تلقائيًا.", @@ -122,6 +98,23 @@ "loading": "جارٍ التحميل...", "sectionTitle": "إدارة النظام", "sectionDescription": "إدارة وكيل الشبكة وتحديثات التطبيق وتفضيلات اللغة.", + "openTargetTitle": "فتح الملفات", + "defaultOpenTarget": "هدف الفتح الافتراضي", + "defaultOpenTargetDescription": "الموقع الافتراضي لفتح الملفات والمجلدات", + "openTargetDesktopOnly": "فتح الملفات في محرر أصلي متاح فقط في تطبيق سطح المكتب.", + "openTargetSaveFailed": "فشل حفظ هدف الفتح: {message}", + "webFileOpenMethod": "طريقة فتح ملفات الويب", + "webFileOpenMethodDescription": "الطريقة الافتراضية لفتح ملفات HTML وملفات الويب الأخرى", + "webFileOpenMethodSaveFailed": "فشل حفظ إعدادات فتح ملفات الويب: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "مدير الملفات", + "terminal": "الطرفية" + }, + "webFileOpenMethods": { + "browser": "فتح في المتصفح", + "editor": "استخدام هدف الفتح الافتراضي" + }, "proxyTitle": "وكيل الشبكة", "proxyDescription": "عند التفعيل، ستُفضَّل إعدادات هذا الوكيل في طلبات الشبكة اللاحقة (بما في ذلك دردشة ACP وتثبيت الوكلاء وعمليات Git البعيدة).", "loadFailed": "فشل التحميل: {message}", diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 862d4ffc..0753c8ef 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -37,7 +37,6 @@ "title": "Einstellungen", "preferences": "Präferenzen", "nav": { - "general": "Allgemein", "appearance": "Darstellung", "agents": "Agenten", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "Schnellnachrichten" } }, - "GeneralSettings": { - "loading": "Wird geladen...", - "sectionTitle": "Allgemein", - "sectionDescription": "Allgemeine App-Einstellungen konfigurieren.", - "loadFailed": "Laden fehlgeschlagen: {message}", - "openTargetTitle": "Dateien öffnen", - "defaultOpenTarget": "Standardziel zum Öffnen", - "defaultOpenTargetDescription": "Standardort zum Öffnen von Dateien und Ordnern", - "openTargetDesktopOnly": "Das Öffnen in einem nativen Editor ist nur in der Desktop-App verfügbar.", - "openTargetSaveFailed": "Öffnungsziel konnte nicht gespeichert werden: {message}", - "webFileOpenMethod": "Webdateien öffnen", - "webFileOpenMethodDescription": "Standardmethode zum Öffnen von HTML- und anderen Webdateien", - "webFileOpenMethodSaveFailed": "Einstellungen zum Öffnen von Webdateien konnten nicht gespeichert werden: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "Dateimanager", - "terminal": "Terminal" - }, - "webFileOpenMethods": { - "browser": "Im Browser öffnen", - "editor": "Standard-Öffnungsziel verwenden" - } - }, "AppearanceSettings": { "sectionTitle": "Design-Erscheinungsbild", "sectionDescription": "Wähle hell, dunkel oder Systemvorgabe. Einstellungen werden automatisch gespeichert.", @@ -122,6 +98,23 @@ "loading": "Wird geladen...", "sectionTitle": "Systemverwaltung", "sectionDescription": "Verwalte Netzwerk-Proxy, App-Updates und Spracheinstellungen.", + "openTargetTitle": "Dateien öffnen", + "defaultOpenTarget": "Standardziel zum Öffnen", + "defaultOpenTargetDescription": "Standardort zum Öffnen von Dateien und Ordnern", + "openTargetDesktopOnly": "Das Öffnen in einem nativen Editor ist nur in der Desktop-App verfügbar.", + "openTargetSaveFailed": "Öffnungsziel konnte nicht gespeichert werden: {message}", + "webFileOpenMethod": "Webdateien öffnen", + "webFileOpenMethodDescription": "Standardmethode zum Öffnen von HTML- und anderen Webdateien", + "webFileOpenMethodSaveFailed": "Einstellungen zum Öffnen von Webdateien konnten nicht gespeichert werden: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Dateimanager", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Im Browser öffnen", + "editor": "Standard-Öffnungsziel verwenden" + }, "proxyTitle": "Netzwerk-Proxy", "proxyDescription": "Wenn aktiviert, werden nachfolgende Netzwerkanfragen bevorzugt über diesen Proxy ausgeführt (einschließlich ACP-Chat, Agent-Installation und Git-Remote-Operationen).", "loadFailed": "Laden fehlgeschlagen: {message}", diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index e9a95c92..9a4f204e 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -37,7 +37,6 @@ "title": "Settings", "preferences": "Preferences", "nav": { - "general": "General", "appearance": "Appearance", "agents": "Agents", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "Quick Messages" } }, - "GeneralSettings": { - "loading": "Loading...", - "sectionTitle": "General", - "sectionDescription": "Configure default app preferences.", - "loadFailed": "Load failed: {message}", - "openTargetTitle": "File opening", - "defaultOpenTarget": "Default open target", - "defaultOpenTargetDescription": "Default location for opening files and folders", - "openTargetDesktopOnly": "Opening files in a native editor is available in the desktop app.", - "openTargetSaveFailed": "Failed to save open target settings: {message}", - "webFileOpenMethod": "Web file opening", - "webFileOpenMethodDescription": "Default method for opening HTML and other web files", - "webFileOpenMethodSaveFailed": "Failed to save web file opening settings: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "File manager", - "terminal": "Terminal" - }, - "webFileOpenMethods": { - "browser": "Open in browser", - "editor": "Use default open target" - } - }, "AppearanceSettings": { "sectionTitle": "Theme Appearance", "sectionDescription": "Choose light, dark, or follow system. Settings are saved automatically.", @@ -122,6 +98,23 @@ "loading": "Loading...", "sectionTitle": "System Management", "sectionDescription": "Manage network proxy, app updates and language preferences.", + "openTargetTitle": "File opening", + "defaultOpenTarget": "Default open target", + "defaultOpenTargetDescription": "Default location for opening files and folders", + "openTargetDesktopOnly": "Opening files in a native editor is available in the desktop app.", + "openTargetSaveFailed": "Failed to save open target settings: {message}", + "webFileOpenMethod": "Web file opening", + "webFileOpenMethodDescription": "Default method for opening HTML and other web files", + "webFileOpenMethodSaveFailed": "Failed to save web file opening settings: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "File manager", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Open in browser", + "editor": "Use default open target" + }, "proxyTitle": "Network Proxy", "proxyDescription": "When enabled, subsequent network requests prefer this proxy (including ACP chat, agent installation and Git remote operations).", "loadFailed": "Load failed: {message}", diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index bba2d0be..6a5b514d 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -37,7 +37,6 @@ "title": "Configuración", "preferences": "Preferencias", "nav": { - "general": "General", "appearance": "Apariencia", "agents": "Agentes", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "Mensajes rápidos" } }, - "GeneralSettings": { - "loading": "Cargando...", - "sectionTitle": "General", - "sectionDescription": "Configura las preferencias generales de la aplicación.", - "loadFailed": "Error al cargar: {message}", - "openTargetTitle": "Apertura de archivos", - "defaultOpenTarget": "Destino predeterminado de apertura", - "defaultOpenTargetDescription": "Ubicación predeterminada para abrir archivos y carpetas", - "openTargetDesktopOnly": "Abrir archivos en un editor nativo solo está disponible en la app de escritorio.", - "openTargetSaveFailed": "No se pudo guardar el destino de apertura: {message}", - "webFileOpenMethod": "Apertura de archivos web", - "webFileOpenMethodDescription": "Método predeterminado para abrir HTML y otros archivos web", - "webFileOpenMethodSaveFailed": "No se pudo guardar la configuración de apertura de archivos web: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "Gestor de archivos", - "terminal": "Terminal" - }, - "webFileOpenMethods": { - "browser": "Abrir en el navegador", - "editor": "Usar destino de apertura predeterminado" - } - }, "AppearanceSettings": { "sectionTitle": "Apariencia del tema", "sectionDescription": "Elige claro, oscuro o seguir el sistema. La configuración se guarda automáticamente.", @@ -122,6 +98,23 @@ "loading": "Cargando...", "sectionTitle": "Administración del sistema", "sectionDescription": "Administra el proxy de red, las actualizaciones de la app y las preferencias de idioma.", + "openTargetTitle": "Apertura de archivos", + "defaultOpenTarget": "Destino predeterminado de apertura", + "defaultOpenTargetDescription": "Ubicación predeterminada para abrir archivos y carpetas", + "openTargetDesktopOnly": "Abrir archivos en un editor nativo solo está disponible en la app de escritorio.", + "openTargetSaveFailed": "No se pudo guardar el destino de apertura: {message}", + "webFileOpenMethod": "Apertura de archivos web", + "webFileOpenMethodDescription": "Método predeterminado para abrir HTML y otros archivos web", + "webFileOpenMethodSaveFailed": "No se pudo guardar la configuración de apertura de archivos web: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Gestor de archivos", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Abrir en el navegador", + "editor": "Usar destino de apertura predeterminado" + }, "proxyTitle": "Proxy de red", "proxyDescription": "Cuando está activado, las solicitudes de red posteriores usarán este proxy preferentemente (incluyendo chat ACP, instalación de agentes y operaciones remotas de Git).", "loadFailed": "Error al cargar: {message}", diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 5a63fbd2..9aa10eb0 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -37,7 +37,6 @@ "title": "Paramètres", "preferences": "Préférences", "nav": { - "general": "Général", "appearance": "Apparence", "agents": "Agents IA", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "Messages rapides" } }, - "GeneralSettings": { - "loading": "Chargement...", - "sectionTitle": "Général", - "sectionDescription": "Configurez les préférences générales de l’application.", - "loadFailed": "Échec du chargement : {message}", - "openTargetTitle": "Ouverture de fichiers", - "defaultOpenTarget": "Cible d’ouverture par défaut", - "defaultOpenTargetDescription": "Emplacement par défaut pour ouvrir les fichiers et dossiers", - "openTargetDesktopOnly": "L’ouverture dans un éditeur natif est disponible uniquement dans l’app de bureau.", - "openTargetSaveFailed": "Échec de l’enregistrement de la cible d’ouverture : {message}", - "webFileOpenMethod": "Ouverture des fichiers web", - "webFileOpenMethodDescription": "Méthode par défaut pour ouvrir les fichiers HTML et autres fichiers web", - "webFileOpenMethodSaveFailed": "Échec de l’enregistrement de l’ouverture des fichiers web : {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "Gestionnaire de fichiers", - "terminal": "Terminal" - }, - "webFileOpenMethods": { - "browser": "Ouvrir dans le navigateur", - "editor": "Utiliser la cible d’ouverture par défaut" - } - }, "AppearanceSettings": { "sectionTitle": "Apparence du thème", "sectionDescription": "Choisissez clair, sombre ou suivre le système. Les paramètres sont enregistrés automatiquement.", @@ -122,6 +98,23 @@ "loading": "Chargement...", "sectionTitle": "Gestion du système", "sectionDescription": "Gérez le proxy réseau, les mises à jour de l’app et les préférences de langue.", + "openTargetTitle": "Ouverture de fichiers", + "defaultOpenTarget": "Cible d’ouverture par défaut", + "defaultOpenTargetDescription": "Emplacement par défaut pour ouvrir les fichiers et dossiers", + "openTargetDesktopOnly": "L’ouverture dans un éditeur natif est disponible uniquement dans l’app de bureau.", + "openTargetSaveFailed": "Échec de l’enregistrement de la cible d’ouverture : {message}", + "webFileOpenMethod": "Ouverture des fichiers web", + "webFileOpenMethodDescription": "Méthode par défaut pour ouvrir les fichiers HTML et autres fichiers web", + "webFileOpenMethodSaveFailed": "Échec de l’enregistrement de l’ouverture des fichiers web : {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Gestionnaire de fichiers", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Ouvrir dans le navigateur", + "editor": "Utiliser la cible d’ouverture par défaut" + }, "proxyTitle": "Proxy réseau", "proxyDescription": "Lorsqu’il est activé, les requêtes réseau suivantes utilisent ce proxy en priorité (y compris le chat ACP, l’installation d’agents et les opérations Git distantes).", "loadFailed": "Échec du chargement : {message}", diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index 04ec45f5..7c14d082 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -37,7 +37,6 @@ "title": "設定", "preferences": "環境設定", "nav": { - "general": "一般", "appearance": "外観", "agents": "エージェント", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "クイックメッセージ" } }, - "GeneralSettings": { - "loading": "読み込み中...", - "sectionTitle": "一般", - "sectionDescription": "アプリの一般設定を構成します。", - "loadFailed": "読み込みに失敗しました: {message}", - "openTargetTitle": "ファイルを開く", - "defaultOpenTarget": "デフォルトの開き先", - "defaultOpenTargetDescription": "ファイルとフォルダーを開くデフォルトの場所", - "openTargetDesktopOnly": "ネイティブエディターでファイルを開く機能はデスクトップアプリでのみ利用できます。", - "openTargetSaveFailed": "開き先設定の保存に失敗しました: {message}", - "webFileOpenMethod": "Web ファイルの開き方", - "webFileOpenMethodDescription": "HTML などのファイルを開く既定の方法", - "webFileOpenMethodSaveFailed": "Web ファイルの開き方設定の保存に失敗しました: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "ファイルマネージャー", - "terminal": "ターミナル" - }, - "webFileOpenMethods": { - "browser": "ブラウザーで開く", - "editor": "デフォルトの開き先を使用" - } - }, "AppearanceSettings": { "sectionTitle": "テーマ外観", "sectionDescription": "ライト、ダーク、またはシステム追従を選択できます。設定は自動保存されます。", @@ -122,6 +98,23 @@ "loading": "読み込み中...", "sectionTitle": "システム管理", "sectionDescription": "ネットワークプロキシ、アプリ更新、言語設定を管理します。", + "openTargetTitle": "ファイルを開く", + "defaultOpenTarget": "デフォルトの開き先", + "defaultOpenTargetDescription": "ファイルとフォルダーを開くデフォルトの場所", + "openTargetDesktopOnly": "ネイティブエディターでファイルを開く機能はデスクトップアプリでのみ利用できます。", + "openTargetSaveFailed": "開き先設定の保存に失敗しました: {message}", + "webFileOpenMethod": "Web ファイルの開き方", + "webFileOpenMethodDescription": "HTML などのファイルを開く既定の方法", + "webFileOpenMethodSaveFailed": "Web ファイルの開き方設定の保存に失敗しました: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "ファイルマネージャー", + "terminal": "ターミナル" + }, + "webFileOpenMethods": { + "browser": "ブラウザーで開く", + "editor": "デフォルトの開き先を使用" + }, "proxyTitle": "ネットワークプロキシ", "proxyDescription": "有効にすると、以降のネットワークリクエストはこのプロキシを優先して使用します(ACP チャット、エージェントのインストール、Git リモート操作を含む)。", "loadFailed": "読み込みに失敗しました: {message}", diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index c7846222..f9aabaf8 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -37,7 +37,6 @@ "title": "설정", "preferences": "환경설정", "nav": { - "general": "일반", "appearance": "외관", "agents": "에이전트", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "빠른 메시지" } }, - "GeneralSettings": { - "loading": "로딩 중...", - "sectionTitle": "일반", - "sectionDescription": "앱의 일반 환경설정을 구성합니다.", - "loadFailed": "불러오기 실패: {message}", - "openTargetTitle": "파일 열기", - "defaultOpenTarget": "기본 열기 대상", - "defaultOpenTargetDescription": "파일과 폴더를 열 기본 위치", - "openTargetDesktopOnly": "네이티브 편집기에서 파일 열기는 데스크톱 앱에서만 사용할 수 있습니다.", - "openTargetSaveFailed": "열기 대상 설정 저장 실패: {message}", - "webFileOpenMethod": "웹 파일 열기 방식", - "webFileOpenMethodDescription": "HTML 등 파일의 기본 열기 방식", - "webFileOpenMethodSaveFailed": "웹 파일 열기 설정 저장 실패: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "파일 관리자", - "terminal": "터미널" - }, - "webFileOpenMethods": { - "browser": "브라우저에서 열기", - "editor": "기본 열기 대상 사용" - } - }, "AppearanceSettings": { "sectionTitle": "테마 모양", "sectionDescription": "라이트, 다크 또는 시스템 설정을 선택할 수 있습니다. 설정은 자동으로 저장됩니다.", @@ -122,6 +98,23 @@ "loading": "로딩 중...", "sectionTitle": "시스템 관리", "sectionDescription": "네트워크 프록시, 앱 업데이트, 언어 설정을 관리합니다.", + "openTargetTitle": "파일 열기", + "defaultOpenTarget": "기본 열기 대상", + "defaultOpenTargetDescription": "파일과 폴더를 열 기본 위치", + "openTargetDesktopOnly": "네이티브 편집기에서 파일 열기는 데스크톱 앱에서만 사용할 수 있습니다.", + "openTargetSaveFailed": "열기 대상 설정 저장 실패: {message}", + "webFileOpenMethod": "웹 파일 열기 방식", + "webFileOpenMethodDescription": "HTML 등 파일의 기본 열기 방식", + "webFileOpenMethodSaveFailed": "웹 파일 열기 설정 저장 실패: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "파일 관리자", + "terminal": "터미널" + }, + "webFileOpenMethods": { + "browser": "브라우저에서 열기", + "editor": "기본 열기 대상 사용" + }, "proxyTitle": "네트워크 프록시", "proxyDescription": "활성화하면 이후 네트워크 요청에서 이 프록시를 우선 사용합니다(ACP 채팅, 에이전트 설치, Git 원격 작업 포함).", "loadFailed": "불러오기 실패: {message}", diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 628ddd31..b984e56d 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -37,7 +37,6 @@ "title": "Configurações", "preferences": "Preferências", "nav": { - "general": "Geral", "appearance": "Aparência", "agents": "Agentes", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "Mensagens rápidas" } }, - "GeneralSettings": { - "loading": "Carregando...", - "sectionTitle": "Geral", - "sectionDescription": "Configure as preferências gerais do app.", - "loadFailed": "Falha ao carregar: {message}", - "openTargetTitle": "Abertura de arquivos", - "defaultOpenTarget": "Destino padrão de abertura", - "defaultOpenTargetDescription": "Local padrão para abrir arquivos e pastas", - "openTargetDesktopOnly": "Abrir arquivos em um editor nativo está disponível apenas no app desktop.", - "openTargetSaveFailed": "Falha ao salvar o destino de abertura: {message}", - "webFileOpenMethod": "Abertura de arquivos web", - "webFileOpenMethodDescription": "Método padrão para abrir HTML e outros arquivos web", - "webFileOpenMethodSaveFailed": "Falha ao salvar as configurações de abertura de arquivos web: {message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "Gerenciador de arquivos", - "terminal": "Terminal" - }, - "webFileOpenMethods": { - "browser": "Abrir no navegador", - "editor": "Usar destino padrão de abertura" - } - }, "AppearanceSettings": { "sectionTitle": "Aparência do tema", "sectionDescription": "Escolha claro, escuro ou seguir o sistema. As configurações são salvas automaticamente.", @@ -122,6 +98,23 @@ "loading": "Carregando...", "sectionTitle": "Gerenciamento do sistema", "sectionDescription": "Gerencie proxy de rede, atualizações do app e preferências de idioma.", + "openTargetTitle": "Abertura de arquivos", + "defaultOpenTarget": "Destino padrão de abertura", + "defaultOpenTargetDescription": "Local padrão para abrir arquivos e pastas", + "openTargetDesktopOnly": "Abrir arquivos em um editor nativo está disponível apenas no app desktop.", + "openTargetSaveFailed": "Falha ao salvar o destino de abertura: {message}", + "webFileOpenMethod": "Abertura de arquivos web", + "webFileOpenMethodDescription": "Método padrão para abrir HTML e outros arquivos web", + "webFileOpenMethodSaveFailed": "Falha ao salvar as configurações de abertura de arquivos web: {message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "Gerenciador de arquivos", + "terminal": "Terminal" + }, + "webFileOpenMethods": { + "browser": "Abrir no navegador", + "editor": "Usar destino padrão de abertura" + }, "proxyTitle": "Proxy de rede", "proxyDescription": "Quando ativado, as solicitações de rede seguintes priorizam este proxy (incluindo chat ACP, instalação de agentes e operações remotas do Git).", "loadFailed": "Falha ao carregar: {message}", diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index fc5c0a34..5b330d82 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -37,7 +37,6 @@ "title": "设置", "preferences": "偏好设置", "nav": { - "general": "常规", "appearance": "外观", "agents": "智能体", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "快捷消息" } }, - "GeneralSettings": { - "loading": "加载中...", - "sectionTitle": "常规", - "sectionDescription": "配置应用的通用偏好设置。", - "loadFailed": "加载失败:{message}", - "openTargetTitle": "文件打开", - "defaultOpenTarget": "默认打开目标", - "defaultOpenTargetDescription": "打开文件和文件夹的默认位置", - "openTargetDesktopOnly": "在原生编辑器中打开文件仅桌面应用可用。", - "openTargetSaveFailed": "打开目标设置保存失败:{message}", - "webFileOpenMethod": "网页文件打开方式", - "webFileOpenMethodDescription": "html等文件默认打开方式", - "webFileOpenMethodSaveFailed": "网页文件打开方式保存失败:{message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "文件管理器", - "terminal": "终端" - }, - "webFileOpenMethods": { - "browser": "在浏览器中打开", - "editor": "使用默认打开目标" - } - }, "AppearanceSettings": { "sectionTitle": "主题外观", "sectionDescription": "选择浅色、深色或跟随系统主题,设置会自动保存。", @@ -122,6 +98,23 @@ "loading": "加载中...", "sectionTitle": "系统管理", "sectionDescription": "管理网络代理、应用升级与语言偏好。", + "openTargetTitle": "文件打开", + "defaultOpenTarget": "默认打开目标", + "defaultOpenTargetDescription": "打开文件和文件夹的默认位置", + "openTargetDesktopOnly": "在原生编辑器中打开文件仅桌面应用可用。", + "openTargetSaveFailed": "打开目标设置保存失败:{message}", + "webFileOpenMethod": "网页文件打开方式", + "webFileOpenMethodDescription": "html等文件默认打开方式", + "webFileOpenMethodSaveFailed": "网页文件打开方式保存失败:{message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "文件管理器", + "terminal": "终端" + }, + "webFileOpenMethods": { + "browser": "在浏览器中打开", + "editor": "使用默认打开目标" + }, "proxyTitle": "网络代理", "proxyDescription": "开启后,后续网络请求将优先走该代理(包括 ACP 对话、Agent 安装、Git 远程操作等)。", "loadFailed": "加载失败:{message}", diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 14540ee1..2b473bc5 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -37,7 +37,6 @@ "title": "設定", "preferences": "偏好設定", "nav": { - "general": "常規", "appearance": "外觀", "agents": "智能體", "mcp": "MCP", @@ -52,29 +51,6 @@ "quick_messages": "快捷訊息" } }, - "GeneralSettings": { - "loading": "載入中...", - "sectionTitle": "常規", - "sectionDescription": "配置應用程式的一般偏好設定。", - "loadFailed": "載入失敗:{message}", - "openTargetTitle": "檔案開啟", - "defaultOpenTarget": "預設開啟目標", - "defaultOpenTargetDescription": "開啟檔案和資料夾的預設位置", - "openTargetDesktopOnly": "在原生編輯器中開啟檔案僅桌面應用程式可用。", - "openTargetSaveFailed": "開啟目標設定儲存失敗:{message}", - "webFileOpenMethod": "網頁檔案開啟方式", - "webFileOpenMethodDescription": "HTML 等檔案的預設開啟方式", - "webFileOpenMethodSaveFailed": "網頁檔案開啟方式儲存失敗:{message}", - "openTargets": { - "vscode": "VS Code", - "file_manager": "檔案管理器", - "terminal": "終端" - }, - "webFileOpenMethods": { - "browser": "在瀏覽器中開啟", - "editor": "使用預設開啟目標" - } - }, "AppearanceSettings": { "sectionTitle": "主題外觀", "sectionDescription": "選擇淺色、深色或跟隨系統主題,設定會自動儲存。", @@ -122,6 +98,23 @@ "loading": "載入中...", "sectionTitle": "系統管理", "sectionDescription": "管理網路代理、應用升級與語言偏好。", + "openTargetTitle": "檔案開啟", + "defaultOpenTarget": "預設開啟目標", + "defaultOpenTargetDescription": "開啟檔案和資料夾的預設位置", + "openTargetDesktopOnly": "在原生編輯器中開啟檔案僅桌面應用程式可用。", + "openTargetSaveFailed": "開啟目標設定儲存失敗:{message}", + "webFileOpenMethod": "網頁檔案開啟方式", + "webFileOpenMethodDescription": "HTML 等檔案的預設開啟方式", + "webFileOpenMethodSaveFailed": "網頁檔案開啟方式儲存失敗:{message}", + "openTargets": { + "vscode": "VS Code", + "file_manager": "檔案管理器", + "terminal": "終端" + }, + "webFileOpenMethods": { + "browser": "在瀏覽器中開啟", + "editor": "使用預設開啟目標" + }, "proxyTitle": "網路代理", "proxyDescription": "啟用後,後續網路請求將優先走該代理(包含 ACP 對話、Agent 安裝、Git 遠端操作等)。", "loadFailed": "載入失敗:{message}", diff --git a/src/lib/api.ts b/src/lib/api.ts index a8de5cab..0d330c0f 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1128,7 +1128,6 @@ export async function openCommitWindow(folderId: number): Promise { } export type SettingsSection = - | "general" | "appearance" | "agents" | "mcp" @@ -1158,7 +1157,7 @@ export async function openSettingsWindow( agentType: options?.agentType ?? null, } ) - window.open(result.path, `settings-${section ?? "general"}`) + window.open(result.path, `settings-${section ?? "system"}`) } export async function openProjectBootWindow(source?: string): Promise { diff --git a/src/lib/tauri.ts b/src/lib/tauri.ts index aa536022..9053cbe7 100644 --- a/src/lib/tauri.ts +++ b/src/lib/tauri.ts @@ -881,7 +881,6 @@ export async function openCommitWindow(folderId: number): Promise { } export type SettingsSection = - | "general" | "appearance" | "agents" | "mcp"