Commit Graph

188 Commits

  • revert: restore full config overwrite + Common Config Snippet (revert 992dda5c)
    Revert the partial key-field merging refactoring introduced in 992dda5c,
    along with two dependent commits (24fa8a18, 87604b18) that referenced
    the now-removed ClaudeQuickToggles component.
    
    The whitelist-based partial merge approach had critical issues:
    - Non-whitelisted custom fields were lost during provider switching
    - Backfill permanently stripped non-key fields from the database
    - Whitelist required constant maintenance to track upstream changes
    
    This restores the proven "full config overwrite + Common Config Snippet"
    architecture where each provider stores its complete configuration and
    shared settings are managed via a separate snippet mechanism.
    
    Reverted commits:
    - 24fa8a18: context-aware JSON editor hint + hide quick toggles
    - 87604b18: hide ClaudeQuickToggles when creating
    - 992dda5c: partial key-field merging refactoring
    
    Restored:
    - Full config snapshot write (write_live_snapshot) for Claude/Codex/Gemini
    - Full config backfill (settings_config = live_config)
    - Common Config Snippet UI and backend commands
    - 6 frontend components/hooks for common config editing
    - configApi barrel export and DB snippet methods
    
    Removed:
    - ClaudeQuickToggles component
    - write_live_partial / backfill_key_fields / patch_claude_live
    - All KEY_FIELDS constants
  • fix: add import button for OpenCode/OpenClaw empty state and remove auto-import on startup
    Previously OpenCode and OpenClaw auto-imported providers from live config
    on app startup, which could confuse users. Now they follow the same
    pattern as Claude/Codex/Gemini: manual import via the empty state button.
  • fix: treat missing db file as error in manual backup to prevent false success toast
    backup_database_file() returning Ok(None) was silently resolved as null
    on the frontend, bypassing try/catch and showing a success toast without
    actually creating a backup file. Now None is converted to an explicit
    Err so the frontend correctly displays an error toast.
  • fix: remove last-provider deletion restriction for OMO/OMO Slim plugins
    OMO and OMO Slim are OpenCode plugins, not standalone apps — users
    should be able to fully remove them. Remove the count-based guard that
    prevented deleting the last active provider, and clean up the now-unused
    provider-count API surface across the full stack.
  • refactor: remove OMO common config two-layer merge system
    Each OMO provider now stores its complete configuration directly in
    settings_config.otherFields instead of relying on a shared OmoGlobalConfig
    merged at write time. This simplifies the data flow from a 4-tuple
    (agents, categories, otherFields, useCommonConfig) to a 3-tuple and
    eliminates an entire DB table, two Tauri commands, and ~1700 lines of
    merge/sync code across frontend and backend.
    
    Backend:
    - Delete database/dao/omo.rs (OmoGlobalConfig struct + get/save methods)
    - Remove get/set_config_snippet from settings DAO
    - Remove get/set_common_config_snippet Tauri commands
    - Replace merge_config() with build_config() in services/omo.rs
    - Simplify OmoVariant (remove config_key, known_keys)
    - Simplify import_from_local and build_local_file_data
    - Rewrite all OMO service tests
    
    Frontend:
    - Delete OmoCommonConfigEditor.tsx and OmoGlobalConfigFields.tsx
    - Delete src/lib/api/config.ts
    - Remove OmoGlobalConfig type and merge preview functions
    - Remove useGlobalConfig/useSaveGlobalConfig query hooks
    - Simplify useOmoDraftState (remove all common config state)
    - Replace OmoCommonConfigEditor with read-only JsonEditor preview
    - Clean i18n keys (zh/en/ja)
  • fix: invalidate OMO Slim query cache after provider mutations
    OMO Slim queries (["omo-slim", ...]) were not invalidated alongside
    OMO queries, causing stale UI state when switching/adding/deleting
    OMO Slim providers.
  • fix(opencode): add missing omo-slim category checks across add/form/mutation paths
    Several code paths only checked for "omo" category but missed "omo-slim",
    causing OMO Slim providers to be treated as regular OpenCode providers
    (triggering invalid write_live_snapshot, requiring manual provider key,
    and showing wrong form fields).
  • feat(workspace): make directory paths clickable and rename "Today's Note" to "Add Memory"
    Add open_workspace_directory Tauri command to open workspace/memory dirs
    in the system file manager. Rename dailyMemory.createToday across all locales.
  • feat(workspace): add full-text search for daily memory files
    Add backend search command that performs case-insensitive matching
    across all daily memory files, supporting both date and content queries.
    Frontend includes animated search bar (⌘F), debounced input, snippet
    display with match count badge, and search state preservation across
    edits.
  • refactor(provider): switch from full config overwrite to partial key-field merging (#1098)
    * refactor(provider): switch from full config overwrite to partial key-field merging
    
    Replace the provider switching mechanism for Claude/Codex/Gemini from
    full settings_config overwrite to partial key-field replacement, preserving
    user's non-provider settings (plugins, MCP, permissions, etc.) across switches.
    
    - Add write_live_partial() with per-app implementations for Claude (JSON env
      merge), Codex (auth replace + TOML partial merge), and Gemini (env merge)
    - Add backfill_key_fields() to extract only provider-specific fields when
      saving live config back to provider entries
    - Update switch_normal, sync_current_to_live, add, update to use partial merge
    - Remove common config snippet feature for Claude/Codex/Gemini (no longer
      needed with partial merging); preserve OMO common config
    - Delete 6 frontend files (3 components + 3 hooks), clean up 11 modified files
    - Remove backend extract_common_config_* methods, 3 Tauri commands,
      CommonConfigSnippets struct, and related migration code
    - Update integration tests to validate key-field-only backfill behavior
    
    * refactor(cleanup): remove dead code and redundant MCP sync after partial-merge refactor
    
    - Remove ConfigService legacy full-overwrite sync methods (~150 lines)
    - Remove redundant McpService::sync_all_enabled from switch_normal
    - Switch proxy fallback recovery from write_live_snapshot to write_live_partial
    - Remove dead ProviderService::write_gemini_live wrapper
    - Update tests to reflect partial-merge behavior (MCP preserved, not re-synced)
    
    * feat(claude): add Quick Toggles for common Claude Code preferences
    
    Add checkbox toggles for hideAttribution, alwaysThinking, and
    enableTeammates that write directly to the live settings file via
    RFC 7396 JSON Merge Patch. Mirror changes to the form editor using
    form.watch for reactive updates.
    
    * fix(provider): add missing key fields to partial-merge constants
    
    Add provider-specific fields verified against official docs to prevent
    key residue or loss during provider switching:
    
    - Claude: CLAUDE_CODE_SUBAGENT_MODEL (env), model (top-level)
    - Codex: review_model, plan_mode_reasoning_effort
    - Gemini: GOOGLE_API_KEY (official alternative to GEMINI_API_KEY)
    
    * fix(provider): expand partial-merge key fields for Bedrock, Vertex, Foundry and behavior settings
    
    Add missing env/top-level fields to CLAUDE_KEY_ENV_FIELDS and
    CLAUDE_KEY_TOP_LEVEL so that provider switching correctly replaces
    (and clears) credentials and flags for AWS Bedrock, Google Vertex AI,
    Microsoft Foundry, and provider behavior overrides like max output
    tokens and prompt caching.
    
    * feat(provider): add auth field selector for Claude providers (AUTH_TOKEN / API_KEY)
    
    Allow users to choose between ANTHROPIC_AUTH_TOKEN and ANTHROPIC_API_KEY
    when creating or editing custom Claude providers, persisted in meta.apiKeyField.
    
    * refactor(preset): remove AiHubMix hardcoded API_KEY in favor of generic auth selector
    
    AiHubMix was the only preset that hardcoded ANTHROPIC_API_KEY before the
    generic auth field selector was introduced. Now that users can freely
    choose between AUTH_TOKEN and API_KEY via the UI, remove the special-case
    and default AiHubMix to the standard ANTHROPIC_AUTH_TOKEN.
  • feat(backup): add independent backup panel, configurable policy, and rename support
    Extract backup & restore into a standalone AccordionItem in Advanced settings.
    Add configurable auto-backup interval (disabled/6h/12h/24h/48h/7d) and retention
    count (3-50) via settings. Add per-backup rename with inline editing UI.
  • feat(backup): add pre-migration backup, periodic backup, backfill warning, and backup management UI
    Four improvements to the database backup mechanism:
    
    1. Auto backup before schema migration - creates a snapshot when
       upgrading from an older database version, providing a safety net
       beyond the existing SAVEPOINT rollback mechanism.
    
    2. Periodic startup backup - checks on app launch whether the latest
       backup is older than 24 hours and creates a new one if needed,
       ensuring all users have recent backups regardless of usage patterns.
    
    3. Backfill failure notification - switch now returns SwitchResult with
       warnings instead of silently ignoring backfill errors, so users are
       informed when their manual config changes may not have been saved.
    
    4. Backup management UI - new BackupListSection in Settings > Data
       Management showing all backup snapshots with restore capability,
       including a confirmation dialog and automatic safety backup before
       restore.
  • feat: more granular local environment checks (#870)
    * feat: more granular local environment checks
    
    * refactor: improve PR #870 with i18n, shadcn Select, and testable helpers
    
    - Extract is_valid_shell, is_valid_shell_flag, default_flag_for_shell
      to module-level #[cfg(windows)] functions for testability
    - Add unit tests for extracted helper functions
    - Replace native <select> with shadcn/ui Select components
    - Extract env badge ternary to ENV_BADGE_CONFIG Record lookup
    - Add i18n keys for env badges and WSL selectors (zh/en/ja)
    - Unify initial useEffect load path with loadAllToolVersions()
    
    * fix: prevent useEffect re-firing on wslShellByTool changes
    
    The useEffect that loads initial tool versions depended on
    loadAllToolVersions, which in turn depended on wslShellByTool.
    This caused a full re-fetch of all 4 tools every time the user
    changed a WSL shell or flag, racing with the single-tool refresh.
    
    Fix: use empty deps [] since this is a mount-only effect. The
    refresh button and shell/flag handlers cover subsequent updates.
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix(query): remove auto-import side effect from useProvidersQuery queryFn
    Users now trigger provider import manually via the empty state button,
    keeping queryFn pure and preventing unintended imports on mount/refocus.
  • feat(workspace): add daily memory file management for OpenClaw
    Add browse, edit, create and delete support for daily memory files
    (~/.openclaw/workspace/memory/YYYY-MM-DD.md) in the Workspace panel.
  • feat(settings): add enableLocalProxy toggle to gate main page proxy UI
    New users often accidentally trigger ProxyToggle/FailoverToggle on the
    main page. Add a settings toggle (default off) so the proxy controls
    only appear when explicitly enabled. The proxy service start/stop in
    settings remains independent of this visibility flag.
  • refactor(omo): deduplicate OMO/OMO Slim via OmoVariant parameterization
    Introduce OmoVariant struct with STANDARD/SLIM constants to eliminate
    ~250 lines of copy-pasted code across DAO, service, commands, and
    frontend layers. Adding a new OMO variant now requires only a single
    const declaration instead of duplicating ~400 lines.
  • feat(omo): add OMO Slim (oh-my-opencode-slim) support
    Implement full OMO Slim profile management to align with ai-toolbox:
    - Backend: Slim service methods, DAO, Tauri commands, plugin conflict handling
    - Frontend: types, API, query hooks, form integration with isSlim parameterization
    - Slim variant: 6 agents (no categories), separate config file and plugin name
    - Mutual exclusion: standard OMO and Slim cannot coexist as plugins
    - i18n: zh/en/ja translations for all Slim agent descriptions
  • feat(webdav): follow-up 补齐自动同步与大文件防护 (#1043)
    * feat(webdav): add robust auto sync with failure feedback
    
    (cherry picked from commit bb6760124a62a964b36902c004e173534910728f)
    
    * fix(webdav): enforce bounded download and extraction size
    
    (cherry picked from commit 7777d6ec2b9bba07c8bbba9b04fe3ea6b15e0e79)
    
    * fix(webdav): only show auto-sync callout for auto-source errors
    
    * refactor(webdav): remove services->commands auto-sync dependency
  • Fix/skill zip symlink resolution (#1040)
    * fix(skill): resolve symlinks in ZIP extraction for GitHub repos (#1001)
    
    - detect symlink entries via is_symlink() during ZIP extraction and collect target paths
    
    - add resolve_symlinks_in_dir() to copy symlink target content into link location
    
    - canonicalize base_dir to fix macOS /tmp → /private/tmp path comparison issue
    
    - add path traversal safety check to block symlinks pointing outside repo boundary
    
    - apply symlink resolution to both download_and_extract and extract_local_zip paths
    
    Closes https://github.com/farion1231/cc-switch/issues/1001
    
    * fix(skill): change search to match name and repo instead of description
    
    * feat(skill): support importing skills from ~/.agents/skills/ directory
    
    - Scan ~/.agents/skills/ in scan_unmanaged() for skill discovery
    - Parse ~/.agents/.skill-lock.json to extract repo owner/name metadata
    - Auto-add discovered repos to skill_repos management on import
    - Add path field to UnmanagedSkill to show discovered location in UI
    
    Closes #980
    
    * fix(skill): use metadata name or ZIP filename for root-level SKILL.md imports (#1000)
    
    When a ZIP contains SKILL.md at the root without a wrapper directory,
    the install name was derived from the temp directory name (e.g. .tmpDZKGpF).
    Now falls back to SKILL.md frontmatter name, then ZIP filename stem.
    
    * feat(skill): scan ~/.cc-switch/skills/ for unmanaged skill discovery and import
    
    * refactor(skill): unify scan/import logic with lock file skillPath and repo saving
    
    - Deduplicate scan_unmanaged and import_from_apps using shared source list
    - Replace hand-written AppType match with as_str() and AppType::all()
    - Extract read_skill_name_desc, build_repo_info_from_lock, save_repos_from_lock helpers
    - Add SkillApps::from_labels for building enable state from source labels
    - Parse skillPath from .skill-lock.json for correct readme URLs
    - Save skill repos to skill_repos table in both import and migration paths
    
    * fix(skill): resolve symlink and path traversal issues in ZIP skill import
    
    * fix(skill): separate source path validation and add canonicalization for symlink safety
  • fix(openclaw): address code review findings across P0-P3 issues
    - Add 25 missing i18n keys for OpenClawFormFields in all 3 locales (P0)
    - Replace key={index} with stable crypto.randomUUID() keys in EnvPanel,
      ToolsPanel, and OpenClawFormFields to prevent list state bugs (P1)
    - Exclude openclaw from ProxyToggle/FailoverToggle in App.tsx (P1)
    - Add merge_additive_config() for openclaw/opencode deep link imports (P1)
    - Normalize serde(flatten) field naming to `extra` + HashMap (P2)
    - Add directory existence check in remove_openclaw_provider_from_live (P2)
    - Remove dead code in import_default_config and openclaw API methods (P2)
    - Add duplicate key validation in EnvPanel before save (P2)
    - Add openclawConfigDir to Settings type (P2)
    - Add staleTime to OpenClaw query hooks (P3)
    - Fix type-unsafe delete via destructuring in mutations.ts (P3)
  • refactor(openclaw): migrate config panels to TanStack Query hooks
    Centralize query keys and extract reusable hooks (useOpenClaw.ts),
    replacing manual useState/useEffect load/save patterns in Env, Tools,
    and AgentsDefaults panels for consistency with MCP/Skills modules.
  • feat(openclaw): add Env/Tools/Agents config panels
    - Migrate OpenClaw commands from provider.rs to dedicated commands/openclaw.rs
    - Add backend types and read/write for env, tools, agents.defaults sections
    - Create EnvPanel (API key + custom vars KV editor)
    - Create ToolsPanel (profile selector + allow/deny lists)
    - Create AgentsDefaultsPanel (default model + runtime parameters)
    - Extend App.tsx menu bar with Env/Tools/Agents buttons
    - Remove Prompts button for OpenClaw (overlaps with Workspace AGENTS.md)
  • feat(openclaw): add Workspace Files panel for managing bootstrap md files
    Add a dedicated panel to read/write OpenClaw's 6 workspace bootstrap files
    (AGENTS.md, SOUL.md, USER.md, IDENTITY.md, TOOLS.md, MEMORY.md) directly
    from ~/.openclaw/workspace/, with a whitelist-secured backend and Markdown
    editor UI. Also fix prompt auto-import missing OpenCode/OpenClaw and the
    PromptFormPanel filenameMap type exclusion.
  • perf: remove unnecessary query cache for Tauri local IPC
    Tauri IPC calls are in-process (microsecond latency), so the 5-minute
    staleTime and disabled refetchOnWindowFocus from web app templates are
    counterproductive. Set staleTime to 0 and enable refetchOnWindowFocus
    so external config changes are reflected immediately.
  • feat(openclaw): add default model button and fix sessionsApi export
    - Add "Enable as Default" button to OpenClaw provider cards
    - Place enable button before add/remove button for better UX
    - Disable remove button when provider is set as default
    - Add sessionsApi re-export from api barrel file to fix white screen
    - Add i18n keys for default model feature (zh/en/ja)
  • feat(openclaw): register suggested models to allowlist on provider add
    When adding an OpenClaw provider with suggestedDefaults, automatically
    merge its model catalog into the allowlist and set the default model
    if not already configured.
  • fix(openclaw): update button state and toast message after switch
    Add cache invalidation for openclawLiveProviderIds in useSwitchProviderMutation
    to ensure button state updates correctly after adding/removing providers.
    
    Also fix toast message to show "已添加到配置" for OpenClaw (same as OpenCode).
  • feat(openclaw): add providerKey input field with validation
    - Add providerKey input field in ProviderForm for OpenClaw
    - Implement duplicate key detection by querying existing providers
    - Add format validation (lowercase letters, numbers, hyphens only)
    - Disable field in edit mode (key cannot be changed after creation)
    - Update mutations.ts to support OpenClaw providerKey
    - Add i18n translations for zh/en/ja
  • feat(openclaw): add additive mode frontend support
    - Add getOpenClawLiveProviderIds() API for querying live config
    - Update ProviderList to query OpenClaw live IDs
    - Rename isOpenCodeMode to isAdditiveMode (covers OpenCode + OpenClaw)
    - Update ProviderCard shouldAutoQuery and isActiveProvider logic
    - Update App.tsx onRemoveFromConfig and invalidateQueries for OpenClaw
  • feat(openclaw): add agents.defaults config support
    - Add types for default model config (primary + fallbacks)
    - Add types for model catalog/allowlist with aliases
    - Extend OpenClawModelEntry with cost and contextWindow fields
    - Add Tauri commands: get/set_openclaw_default_model, get/set_openclaw_model_catalog
    - Create frontend API (src/lib/api/openclaw.ts)
    - Add suggestedDefaults to provider presets with model metadata
    - Add i18n translations (zh/en/ja) for openclawConfig.*
  • feat(types): add OpenClaw TypeScript type definitions
    - Add OpenClawProviderConfig and OpenClawModel interfaces
    - Add openclaw to McpApps, VisibleApps, ProxyTakeoverStatus
    - Add "openclaw" to AppId union type
    - Add openclaw to SkillApps interface
  • Webdav (#923)
    * feat: WebDAV backup/restore
    
    - Add WebDAV test/backup/restore commands and settings\n- Fix ja i18n missing keys; decode PROPFIND href as UTF-8\n- Stabilize Windows prompt auto-import tests via CC_SWITCH_TEST_HOME
    
    * chore: format and minor cleanups
    
    * fix: update build config
    
    * feat(webdav): unify sync UX and hardening fixes
    
    * fix(webdav): harden sync flow and stabilize sync UX/tests
    
    * fix(webdav): add resource limits to skills.zip extraction
    
    Prevent zip bomb / resource exhaustion by enforcing:
    - MAX_EXTRACT_ENTRIES (10,000 files)
    - MAX_EXTRACT_BYTES (512 MB cumulative)
    
    * refactor(webdav): drop deviceId and display deviceName only
    
    ---------
    
    Co-authored-by: small-lovely-cat <77799160+small-lovely-cat@users.noreply.github.com>
    Co-authored-by: saladday <1203511142@qq.com>
  • feat(proxy): fix thinking rectifiers and resolve clippy warnings (#1005)
    * feat(proxy): align thinking rectifiers and resolve clippy warnings
    
    - add thinking budget rectifier flow with single retry on anthropic budget errors
    
    - align thinking signature rectification behavior with adaptive-safe handling
    
    - expose requestThinkingBudget in settings/ui/i18n and default rectifier config to disabled
    
    - fix clippy warnings in model_mapper format args and RectifierConfig default derive
    
    * fix(proxy): thinking rectifiers
  • feat(omo): integrate Oh My OpenCode profile management (#972)
    * feat(omo): integrate Oh My OpenCode profile management into Provider system
    
    Adds full-stack OMO support: backend config read/write/import, OMO-specific
    provider CRUD with exclusive switching, frontend profile editor with
    agent/category/model configuration, global config management, and i18n support.
    
    * feat(omo): add model/variant dropdowns from enabled providers
    
    Replace model text inputs with Select dropdowns sourced from enabled
    OpenCode providers, add thinking-level variant selection, and prevent
    auto-enabling newly added OMO providers.
    
    * fix(omo): use standard provider action styles for OMO switch button
    
    * fix(omo): replace hardcoded isZh strings with proper i18n t() calls
  • feat(usage): enhance dashboard with auto-refresh control and robust formatting (#942)
    * style: format code and apply clippy lint fixes
    
    * feat(usage): enhance dashboard with auto-refresh control and robust formatting
    
    - Add configurable auto-refresh interval toggle (off/5s/10s/30s/60s) to usage dashboard
    - Extract shared format utilities (fmtUsd, fmtInt, parseFiniteNumber, getLocaleFromLanguage)
    - Refactor request log time filtering to rolling vs fixed mode with validation
    - Use stable serializable query keys instead of filter objects
    - Handle NaN/Infinity safely in number formatting across all usage components
    - Use RFC 3339 date format in backend trend data
  • refactor(terminal): unify terminal selection using global settings
    - Remove terminal selector from Session Manager page
    - Backend now reads terminal preference from global settings
    - Add terminal name mapping (iterm2 → iterm) for compatibility
    - Add WezTerm support to macOS terminal options
    - Add WezTerm translations for zh/en/ja
  • refactor(ui): extract shared components and deduplicate MCP/Skills panels (#897)
    * refactor(ui): add tooltips and icons to MCP and Skills panels
    
    * refactor: deduplicate UnifiedSkillsPanel and UnifiedMcpPanel shared code
  • feat: session manger (#867)
    * feat: init session manger
    
    * feat: persist selected app to localStorage
    
    - Save app selection when switching providers
    - Restore last selected app on page load
    
    * feat: persist current view to localStorage
    
    - Save view selection when switching tabs
    - Restore last selected view on page load
    
    * styles: update ui
    
    * feat: Improve macOS Terminal activation and refactor Kitty launch to use  command with user's default shell.
    
    * fix: session view
    
    * feat: toc
    
    * feat: Implement FlexSearch for improved session search functionality.
    
    * feat: Redesign session manager search and filter UI for a more compact and dynamic experience.
    
    * refactor: modularize session manager by extracting components and utility functions into dedicated files.
    
    * feat: Enhance session terminal launching with support for iTerm2, Ghostty, WezTerm, and Alacritty, including UI and custom configuration options.
    
    * feat: Conditionally render terminal selection and resume session buttons only on macOS.
  • fix(mutations): use extractErrorMessage for complete error display
    Provider add/update/delete mutations were using error.message directly,
    which doesn't extract Tauri invoke errors properly. Now using the same
    extractErrorMessage pattern as the rest of the codebase.
  • feat(claude): show proxy hint when switching to OpenAI Chat format provider
    Display an info toast when switching to a provider that uses OpenAI Chat
    format (apiFormat === "openai_chat"), reminding users to enable the proxy
    service. Move toast logic from mutation to useProviderActions for better
    control over notification content based on provider properties.
  • feat(skills): add install from ZIP file feature
    - Add open_zip_file_dialog command for selecting ZIP files
    - Add install_from_zip service method with recursive skill scanning
    - Add install_skills_from_zip Tauri command
    - Add frontend API methods and useInstallSkillsFromZip hook
    - Add "Install from ZIP" button in Skills management page
    - Support local skill ID format: local:{directory}
    - Add i18n translations for new feature and error messages
  • Feat/pricing config enhancement (#781)
    * feat(db): add pricing config fields to proxy_config table
    
    - Add default_cost_multiplier field per app type
    - Add pricing_model_source field (request/response)
    - Add request_model field to proxy_request_logs table
    - Implement schema migration v5
    
    * feat(api): add pricing config commands and provider meta fields
    
    - Add get/set commands for default cost multiplier
    - Add get/set commands for pricing model source
    - Extend ProviderMeta with cost_multiplier and pricing_model_source
    - Register new commands in Tauri invoke handler
    
    * fix(proxy): apply cost multiplier to total cost only
    
    - Move multiplier calculation from per-item to total cost
    - Add resolve_pricing_config for provider-level override
    - Include request_model and cost_multiplier in usage logs
    - Return new fields in get_request_logs API
    
    * feat(ui): add pricing config UI and usage log enhancements
    
    - Add pricing config section to provider advanced settings
    - Refactor PricingConfigPanel to compact table layout
    - Display all three apps (Claude/Codex/Gemini) in one view
    - Add multiplier column and request model display to logs
    - Add frontend API wrappers for pricing config
    
    * feat(i18n): add pricing config translations
    
    - Add zh/en/ja translations for pricing defaults config
    - Add translations for multiplier, requestModel, responseModel
    - Add provider pricing config translations
    
    * fix(pricing): align backfill cost calculation with real-time logic
    
    - Fix backfill to deduct cache_read_tokens from input (avoid double billing)
    - Apply multiplier only to total cost, not to each item
    - Add multiplier display in request detail panel with i18n support
    - Use AppError::localized for backend error messages
    - Fix init_proxy_config_rows to use per-app default values
    - Fix silent failure in set_default_cost_multiplier/set_pricing_model_source
    - Add clippy allow annotation for test mutex across await
    
    * style: format code with cargo fmt and prettier
    
    * fix(tests): correct error type assertions in proxy DAO tests
    
    The tests expected AppError::InvalidInput but the DAO functions use
    AppError::localized() which returns AppError::Localized variant.
    Updated assertions to match the correct error type with key validation.
    
    ---------
    
    Co-authored-by: Jason <farion1231@gmail.com>
  • fix(skills): prevent duplicate skill installation from different repos (#778)
    - Add directory conflict detection before installation
    - Fix installed status check to match repo owner and name
    - Add i18n translations for conflict error messages
  • feat(skills): add skill sync method setting (symlink/copy)
    - Add SyncMethod enum (Auto/Symlink/Copy) in Rust backend
    - Implement sync_to_app_dir with symlink support (cross-platform)
    - Add SkillSyncMethodSettings UI component (simplified 2-button selector)
    - Add i18n support for zh/en/ja
    - Replace copy_to_app with configurable sync_to_app_dir
    - Add skill_sync_method field to AppSettings
    
    User can now choose between symlink (disk space saving) or copy (best compatibility) in Settings > General.
  • fix(failover): switch to P1 immediately when enabling auto failover
    Previously, enabling auto failover kept using the current provider until
    the first failure, causing inconsistency when the current provider was
    not in the failover queue. When stopping proxy, the restored config
    would not match user expectations.
    
    New behavior:
    - Enable auto failover = immediately switch to queue P1
    - Subsequent routing follows queue order (P1→P2→...)
    - Auto-add current provider to queue if queue is empty
    
    Changes:
    - Add switch_proxy_target() for hot-switching during proxy mode
    - Update provider_router to use queue order when failover enabled
    - Sync tray menu Auto click with the same logic
    - Update UI tooltips to reflect new semantics
    - Add tests for queue-only routing scenario
  • Feat/provider individual config (#663)
    * refactor(ui): simplify UpdateBadge to minimal dot indicator
    
    * feat(provider): add individual test and proxy config for providers
    
    Add support for provider-specific model test and proxy configurations:
    
    - Add ProviderTestConfig and ProviderProxyConfig types in Rust and TypeScript
    - Create ProviderAdvancedConfig component with collapsible panels
    - Update stream_check service to merge provider config with global config
    - Proxy config UI follows global proxy style (single URL input)
    
    Provider-level configs stored in meta field, no database schema changes needed.
    
    * feat(ui): add failover toggle and improve proxy controls
    
    - Add FailoverToggle component with slide animation
    - Simplify ProxyToggle style to match FailoverToggle
    - Add usage statistics button when proxy is active
    - Fix i18n parameter passing for failover messages
    - Add missing failover translation keys (inQueue, addQueue, priority)
    - Replace AboutSection icon with app logo
    
    * fix(proxy): support system proxy fallback and provider-level proxy config
    
    - Remove no_proxy() calls in http_client.rs to allow system proxy fallback
    - Add get_for_provider() to build HTTP client with provider-specific proxy
    - Update forwarder.rs and stream_check.rs to use provider proxy config
    - Fix EditProviderDialog.tsx to include provider.meta in useMemo deps
    - Add useEffect in ProviderAdvancedConfig.tsx to sync expand state
    
    Fixes #636
    Fixes #583
    
    * fix(ui): sync toast theme with app setting
    
    * feat(settings): add log config management
    
    Fixes #612
    Fixes #514
    
    * fix(proxy): increase request body size limit to 200MB
    
    Fixes #666
    
    * docs(proxy): update timeout config descriptions and defaults
    
    Fixes #612
    
    * fix(proxy): filter x-goog-api-key header to prevent duplication
    
    * fix(proxy): prevent proxy recursion when system proxy points to localhost
    
    Detect if HTTP_PROXY, HTTPS_PROXY, or ALL_PROXY environment variables
    point to loopback addresses (localhost, 127.0.0.1), and bypass system
    proxy in such cases to avoid infinite request loops.
    
    * fix(i18n): add providerAdvanced i18n keys and fix failover toast parameter
    
    - Add providerAdvanced.* i18n keys to en.json, zh.json, and ja.json
    - Fix failover toggleFailed toast to pass detail parameter
    - Remove Chinese fallback text from UI for English/Japanese users
    
    * fix(tray): restore tray-provider events and enable Auto failover properly
    
    - Emit provider-switched event on tray provider click (backward compatibility)
    - Auto button now: starts proxy, takes over live config, enables failover
    
    * fix(log): enable dynamic log level and single file mode
    
    - Initialize log at Trace level for dynamic adjustment
    - Change rotation strategy to KeepSome(1) for single file
    - Set max file size to 1GB
    - Delete old log file on startup for clean start
    
    * fix(tray): fix clippy uninlined format args warning
    
    Use inline format arguments: {app_type_str} instead of {}
    
    * fix(provider): allow typing :// in endpoint URL inputs
    
    Change input type from "url" to "text" to prevent browser
    URL validation from blocking :// input.
    
    Closes #681
    
    * fix(stream-check): use Gemini native streaming API format
    
    - Change endpoint from OpenAI-compatible to native streamGenerateContent
    - Add alt=sse parameter for SSE format response
    - Use x-goog-api-key header instead of Bearer token
    - Convert request body to Gemini contents/parts format
    
    * feat(proxy): add request logging for debugging
    
    Add debug logs for outgoing requests including URL and body content
    with byte size, matching the existing response logging format.
    
    * fix(log): prevent usize underflow in KeepSome rotation strategy
    
    KeepSome(n) internally computes n-2, so n=1 causes underflow.
    Use KeepSome(2) as the minimum safe value.