Commit Graph

720 Commits

  • merge: resolve conflicts between fix/common-config-sync-improvements and main
    Merged both feature sets:
    - common-config: commonConfigEnabled, commonConfigEnabledByApp fields
    - api-format: apiFormat field for Claude provider OpenAI compatibility
    
    Key resolution in live.rs: apply sanitize_claude_settings_for_live()
    to merged config before writing to ensure internal fields are stripped.
  • fix(common-config): improve error handling, layer separation, and reliability across sync system
    P0 Fixes:
    - Enable toml `preserve_order` feature for reliable key ordering
    - Extract pure detection functions to utils/commonConfigDetection.ts (fix layer inversion)
    - Return structured result from preserveCodexConfigFormat to prevent data loss
    
    P1 Fixes:
    - Handle pure TOML strings in Codex config instead of silent skip
    - Surface extractDiff errors in useCodexCommonConfig instead of swallowing
    - Log parse errors in detectCommonConfigEnabledByContent for debugging
    
    P2 Fixes:
    - Use parsing-based assertions in Rust TOML ordering tests
    - Add schema warning when Codex config field is wrong type
    - Add depth/node limits to isSubset function (prevent stack overflow)
    - Replace hardcoded "{}" check with adapter.hasValidContent()
    
    P3 Fixes:
    - Refactor Gemini errors to structured GeminiConfigErrorInfo format
    - Add mapGeminiErrorToI18n for type-safe error-to-i18n mapping
  • 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.
  • chore: bump version to 3.10.3
    - Update version in package.json, Cargo.toml, and tauri.conf.json
    - Add missing changelog entries for OpenCode API key link and AICodeMirror preset
    - Fix Prettier formatting for new hook files
  • feat(opencode): add API key link support for OpenCode provider form
    Enable API key link feature for OpenCode app, allowing users to access
    provider websites for key registration directly from the form.
  • feat(providers): add AICodeMirror partner preset for all apps
    Register AICodeMirror icon, add i18n partner promotion texts (zh/en/ja),
    and fix trailing whitespace in codexProviderPresets.
  • refactor(common-config): consolidate hasContent methods into adapters
    - Add hasContent method to CommonConfigAdapter interface
    - Move isSubset utility to configMerge.ts for reuse
    - Export preserveCodexConfigFormat from adapters for hook use
    - Add hasContentByAppType dispatcher for unified content detection
    - Remove dead code from providerConfigUtils (hasCommonConfigSnippet,
      hasTomlCommonConfigSnippet, hasGeminiCommonConfigSnippet)
  • fix(codex): reorder merged TOML keys to put custom config first
    When merging TOML configs, common config keys were appearing before
    custom config keys. Add reorderMergedKeys() to ensure custom config
    keys (model_provider, model, etc.) appear at the top, with common-only
    keys appended after.
  • feat(settings): add OpenCode support to environment check and one-click install
    - Add OpenCode version detection with Go path scanning
    - Add GitHub Releases API for fetching latest OpenCode version
    - Add OpenCode install command to one-click install section
    - Update i18n hints to include OpenCode across all locales
    - Fix SettingsPage indentation formatting
  • 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(providers): add NVIDIA NIM preset for Claude and OpenCode
    - Add NVIDIA NIM provider preset with API configuration
    - Add nvidia.svg icon and register in icon system
    - Add nvidia metadata with keywords and default color (#74B71B)
  • feat(claude): add apiFormat support for provider presets
    Allow preset providers to specify API format (anthropic or openai_chat),
    enabling third-party proxy services that use OpenAI Chat Completions format.
  • refactor(claude): migrate api_format from settings_config to meta
    Move api_format storage from settings_config to ProviderMeta to prevent
    polluting ~/.claude/settings.json when switching providers.
    
    - Add api_format field to ProviderMeta (Rust + TypeScript)
    - Update ProviderForm to read/write apiFormat from meta
    - Maintain backward compatibility for legacy settings_config.api_format
      and openrouter_compat_mode fields (read-only fallback)
    - Strip api_format from settings_config before writing to live config
  • fix(i18n): update apiFormatOpenAIChat label to mention proxy requirement
    Change label from "Requires conversion" to "Requires proxy" for clarity:
    - zh: "需转换" → "需开启代理"
    - en: "Requires conversion" → "Requires proxy"
    - ja: "変換が必要" → "プロキシが必要"
  • fix(claude): improve backward compatibility for openrouter_compat_mode
    Extend backward compatibility support for legacy openrouter_compat_mode field:
    - Support number type (1 = enabled, 0 = disabled)
    - Support string type ("true"/"1" = enabled)
    - Add corresponding test cases for number and string types
  • feat(claude): add API format selector for third-party providers
    Replace the OpenRouter-specific compatibility toggle with a generic
    API format selector that allows all Claude providers to choose between:
    
    - Anthropic Messages (native): Direct passthrough, no conversion
    - OpenAI Chat Completions: Enables Anthropic ↔ OpenAI format conversion
    
    Changes:
    - Add ClaudeApiFormat type ("anthropic" | "openai_chat") to types.ts
    - Replace openRouterCompatToggle with apiFormat dropdown in ClaudeFormFields
    - Update ProviderForm to manage apiFormat state via settingsConfig.api_format
    - Refactor claude.rs: add get_api_format() method, update needs_transform()
    - Maintain backward compatibility with legacy openrouter_compat_mode field
    - Update i18n translations (zh, en, ja)
  • refactor(common-config): consolidate hooks and migrate Gemini to ENV format
    - Delete redundant wrapper hooks (useCommonConfigSnippet, useGeminiCommonConfig)
    - Change Gemini common config from JSON to ENV format (.env style)
    - Add backend validation with forbidden keys filtering (GEMINI_API_KEY, GOOGLE_GEMINI_BASE_URL)
    - Fix localStorage migration to skip empty parsed snippets
    - Add error handling for silent JSON parse failures
    - Clean up debug logs and unused types
  • fix(test): move orphaned test into describe block
    Move "clears loading flag when all mutations idle" test inside
    the describe("useProviderActions") block for proper test isolation.
  • chore: remove dead code from providerConfigUtils and config API
    - Remove unused functions: updateCommonConfigSnippet, replaceCommonConfigSnippet,
      replaceGeminiCommonConfigSnippet, updateTomlCommonConfigSnippet,
      replaceTomlCommonConfigSnippet and their internal helpers (~600 lines)
    - Remove deprecated getClaudeCommonConfigSnippet and setClaudeCommonConfigSnippet
    - Remove unused imports (deepClone, deepMerge)
  • refactor: unify common config hooks with generic base hook and adapters
    - Create useCommonConfigBase generic hook (~300 lines)
    - Create commonConfigAdapters for Claude (JSON), Codex (TOML), Gemini (ENV/JSON)
    - Refactor three hooks from ~1370 lines to ~430 lines (-940 lines)
    - Extract useDarkMode hook from three ConfigSections components
    - Remove dead code: backend _str functions, frontend JSON/TOML unused exports
    - Deduplicate deepClone/deepMerge utilities
    - Fix duplicate mod tests in provider.rs
  • fix(codex): fix config.toml not updated after extracting common config
    The handleExtract function assumed codexConfig was JSON format, but it's
    actually a pure TOML string from useCodexConfigState. JSON.parse() failed
    silently, causing onConfigChange not to be called.
    
    Changes:
    - Handle both pure TOML string and legacy JSON format
    - Add missing i18n key for zh.json
  • 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(settings): prioritize native install path for Claude Code detection
    - Move ~/.local/bin to first position in version scan search paths
    - Update one-click install commands to use official native installation
      (curl script) instead of npm for Claude Code
  • feat(ui): add auto-height support for JsonEditor component
    Add autoHeight prop to dynamically resize editor based on content lines.
    Apply to config preview sections in Codex, Gemini, and CommonConfig forms.
  • fix(config): defer common config save until form submission
    - Change handleExtract and handleCommonConfigSnippetChange to use delayed save pattern
    - Add hasUnsavedCommonConfig state to track pending changes
    - Add getPendingCommonConfigSnippet and markCommonConfigSaved helper functions
    - Remove immediate backend API calls, save only on form submit
    - Add extractSuccessNeedSave and saveCommonConfigFailed i18n keys
  • fix(settings): correct Gemini default visibility to true (#818)
    Frontend fallback value for visibleApps had gemini: false, which was
    inconsistent with backend default (gemini: true). This caused Gemini
    to be hidden by default on first install.
    
    Fixes #817
    
    Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
  • chore(presets): upgrade Kimi/Moonshot providers to k2.5 model
    - Update Moonshot preset models from kimi-k2-thinking to kimi-k2.5
    - Update OpenCode Kimi preset name and model to k2.5
    - Remove explicit model config from Kimi preset (use API defaults)
  • fix(config): resolve common config sync issues across frontend and backend
    - Fix backfill pollution: extract custom config from live when common config enabled
    - Fix proxy backup: merge common config before backup to preserve full config
    - Unify null override semantics: null no longer overrides in both frontend and backend
    - Add missing i18n keys for common config UI
    - Hide format button in readonly JsonEditor
    - Add mapGeminiWarningToI18n for user-friendly warning messages
  • fix(settings): correct footer layout in advanced settings tab
    - Establish proper flexbox height chain from App to SettingsPage
    - Move save button to fixed footer outside scrollable area
    - Align footer styling with FullScreenPanel for consistency
  • 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>
  • merge: resolve conflict in live.rs, keep write_live_snapshot_with_merge
    - Combine doc comments from both branches
    - Keep write_live_snapshot_with_merge for common config runtime merge
    - Fix indentation to match main branch style
  • fix(config): improve Gemini common config parsing robustness
    - Add ENV format quote stripping (KEY="value" -> value)
    - Add toast warnings for filtered forbidden keys
    - Unify error codes with GEMINI_CONFIG_ERROR_CODES constants
    - Remove duplicate isPlainObject, use shared implementation
    - Fix type guard for filter entries
  • fix(dialog): support wrapped format for Gemini common config
    Address audit finding: EditProviderDialog.tsx:121 only parsed flat JSON
    format, not the wrapped {"env": {...}} format.
    
    Changes:
    - Detect if common config is wrapped or flat format
    - Extract env object from either format
    - Properly compute difference for live settings extraction
    
    This ensures backward compatibility with existing wrapped-format data.
  • fix(form): use JSON parsing for Gemini common config snippet
    Address audit finding: ProviderForm.tsx:944 was using envStringToObj()
    (KEY=VALUE format) to parse Gemini common config, but it's stored as JSON.
    
    Changes:
    - Add parseGeminiCommonConfig() helper function
    - Support both flat {"KEY": "VALUE"} and wrapped {"env": {...}} formats
    - Properly extract difference between custom and common env
    
    This prevents common config values from being incorrectly saved as
    custom config when the formats don't match.
  • fix(form): add type guard for Gemini env values
    Address code audit finding #10:
    
    Replace unsafe type assertion with proper type guard when extracting
    custom env config. Convert all values to strings explicitly using
    String() to prevent runtime type errors when env contains non-string
    values (numbers, booleans, etc).
    
    Before: envObj = customConfig as Record<string, string>
    After: Iterate and convert each value with String(value)
  • refactor(hooks): improve error handling and user feedback
    Address code audit findings #3, #5, #9:
    
    Save queue improvements:
    - Add error logging in enqueueSave() catch handlers
    - Use console.error with [SaveQueue] prefix for debugging
    - Prevent silent failures in async save operations
    
    Extract operation improvements:
    - Add toast.success() notification after extract completes
    - Notify user that custom config was automatically updated
    - Add error logging for failed settingsConfig updates
    
    I18n improvements:
    - Replace MCP i18n keys with dedicated Codex keys:
      - codexConfig.tomlFormatError
      - codexConfig.tomlSyntaxError
      - codexConfig.extractedTomlInvalid
    - Avoid namespace pollution between modules
  • feat(opencode): sync all providers to live config on directory change
    Add additive mode support for OpenCode in sync_current_to_live:
    - Add AppType::is_additive_mode() to distinguish switch vs additive mode
    - Add AppType::all() iterator to avoid hardcoding app lists
    - Add sync_all_providers_to_live() for additive mode apps
    - Refactor sync_current_to_live to handle both modes
    
    Frontend changes (directory settings):
    - Track opencodeDirChanged in useDirectorySettings
    - Trigger syncCurrentProvidersLiveSafe when OpenCode dir changes
    - Add i18n strings for OpenCode directory settings
  • fix(dialog): unify Gemini common config format parsing
    Address code audit finding #4:
    
    The frontend was incorrectly parsing Gemini common config as KEY=VALUE
    format while the backend expects JSON format. Unify to use JSON parsing
    {"KEY": "VALUE"} which matches the format stored by useGeminiCommonConfig.
    
    Also add type guard when converting to string record to prevent type
    assertion issues with non-string values.
  • feat(dialog): extract custom config from live settings on edit
    Update EditProviderDialog to extract custom configuration from live
    settings when common config is enabled, ensuring the edit form shows
    only the provider-specific values.
    
    When loading live settings for editing:
    - Check if common config is enabled for the provider and app type
    - Fetch the common config snippet from database
    - Extract the difference (custom config) from live settings
    - Handle all three formats: Claude (JSON), Codex (TOML), Gemini (env)
    
    This prevents the merged common config values from appearing in the
    edit form, which would cause them to be saved as custom config and
    result in duplicate values after the next merge.
  • feat(form): extract custom config difference on provider save
    Update ProviderForm to extract only the custom (provider-specific)
    configuration when saving, removing common config values that would
    be merged at runtime.
    
    Changes:
    - Import configMerge utilities (extractDifference, extractTomlDifference)
    - Pass finalConfig from hooks to config editor components
    - On save, extract difference between current config and common snippet
    - Apply extraction for all three app types (Claude JSON, Codex TOML, Gemini env)
    
    This ensures the database stores only the provider's unique configuration,
    while the common config is stored separately and merged at runtime when
    writing to live files.
  • feat(ui): add merge preview to common config editor components
    Add real-time merge preview functionality to all config editor components
    (Claude, Codex, Gemini) that shows the final merged configuration when
    common config is enabled.
    
    UI changes:
    - Add finalConfig/finalEnv prop to display merged result
    - Add toggle button to show/hide merge preview (Eye/EyeOff icons)
    - Auto-show preview when common config is enabled
    - Display custom config editor with label when preview is visible
    - Show read-only merged preview below custom config
    - Add visual indicators: "只读" badge, green hint text
    
    The preview helps users understand how their custom configuration
    combines with the shared common config template before saving.
  • refactor(hooks): redesign common config hooks for runtime merge architecture
    Refactor all three common config hooks (Claude, Codex, Gemini) to support
    the new runtime merge architecture where:
    - settingsConfig stores only custom (provider-specific) configuration
    - commonConfigSnippet stores shared template configuration in database
    - finalConfig is computed at runtime: merge(commonConfig, customConfig)
    - Toggling common config only changes enabled state, not settingsConfig
    
    Key changes across all hooks:
    - Add finalConfig/finalEnv return value for merge preview
    - Use configMerge utilities (computeFinalConfig, extractDifference)
    - Remove direct config manipulation on toggle (no more inject/remove)
    - Add proper TypeScript return type interfaces
    - Simplify state management by removing unnecessary refs
    - Improve code organization with clear section comments
    
    This refactor enables:
    - Clean separation between custom and common configuration
    - Real-time merge preview in the UI
    - Consistent behavior across Claude (JSON), Codex (TOML), and Gemini (env)
  • feat(editor): add readOnly mode to JsonEditor component
    Add a readOnly prop to the JsonEditor component that:
    - Enables CodeMirror's EditorState.readOnly extension
    - Applies visual styling (reduced opacity, default cursor)
    - Prevents user modifications to the editor content
    
    This feature is needed for displaying merged config previews where
    users should see the final result but not edit it directly.
  • feat(config): add core utilities for config merge and difference extraction
    Add comprehensive config merge utilities for both Rust backend and TypeScript frontend
    to support the new common config architecture where customConfig overrides commonConfig.
    
    Backend (Rust):
    - Add config_merge.rs module with JSON and TOML merge functions
    - Support deep merge where source overrides target for nested objects
    - Add extract_difference functions to identify custom-only keys
    - Include compute_final_*_config functions for runtime merge calculation
    - Register module in lib.rs
    
    Frontend (TypeScript):
    - Add configMerge.ts with isPlainObject, deepClone, deepEqual, deepMerge utilities
    - Implement computeFinalConfig for merging common + custom configs
    - Add extractDifference to identify keys unique to live config
    - Add tomlConfigMerge.ts with TOML-specific merge/extract functions
    - Use smol-toml for parsing and serialization
    
    These utilities form the foundation for the refactored common config system
    where providers store only custom config and merge with shared templates at runtime.
  • feat(settings): add preferred terminal selection
    Allow users to choose their preferred terminal app for opening
    provider terminals. Supports platform-specific options:
    - macOS: Terminal.app, iTerm2, Alacritty, Kitty, Ghostty
    - Windows: cmd, PowerShell, Windows Terminal
    - Linux: GNOME Terminal, Konsole, Xfce4, Alacritty, Kitty, Ghostty
    
    Falls back to system default if selected terminal is unavailable.