* feat(providers): add notes field for provider management
- Add notes field to Provider model (backend and frontend)
- Display notes with higher priority than URL in provider card
- Style notes as non-clickable text to differentiate from URLs
- Add notes input field in provider form
- Add i18n support (zh/en) for notes field
* chore: format code and clean up unused props
- Run cargo fmt on Rust backend code
- Format TypeScript imports and code style
- Remove unused appId prop from ProviderPresetSelector
- Clean up unused variables in tests
- Integrate notes field handling in provider dialogs
* feat(deeplink): implement ccswitch:// protocol for provider import
Add deep link support to enable one-click provider configuration import via ccswitch:// URLs.
Backend:
- Implement URL parsing and validation (src-tauri/src/deeplink.rs)
- Add Tauri commands for parse and import (src-tauri/src/commands/deeplink.rs)
- Register ccswitch:// protocol in macOS Info.plist
- Add comprehensive unit tests (src-tauri/tests/deeplink_import.rs)
Frontend:
- Create confirmation dialog with security review UI (src/components/DeepLinkImportDialog.tsx)
- Add API wrapper (src/lib/api/deeplink.ts)
- Integrate event listeners in App.tsx
Configuration:
- Update Tauri config for deep link handling
- Add i18n support for Chinese and English
- Include test page for deep link validation (deeplink-test.html)
Files: 15 changed, 1312 insertions(+)
* chore(deeplink): integrate deep link handling into app lifecycle
Wire up deep link infrastructure with app initialization and event handling.
Backend Integration:
- Register deep link module and commands in mod.rs
- Add URL handling in app setup (src-tauri/src/lib.rs:handle_deeplink_url)
- Handle deep links from single instance callback (Windows/Linux CLI)
- Handle deep links from macOS system events
- Add tauri-plugin-deep-link dependency (Cargo.toml)
Frontend Integration:
- Listen for deeplink-import/deeplink-error events in App.tsx
- Update DeepLinkImportDialog component imports
Configuration:
- Enable deep link plugin in tauri.conf.json
- Update Cargo.lock for new dependencies
Localization:
- Add Chinese translations for deep link UI (zh.json)
- Add English translations for deep link UI (en.json)
Files: 9 changed, 359 insertions(+), 18 deletions(-)
* refactor(deeplink): enhance Codex provider template generation
Align deep link import with UI preset generation logic by:
- Adding complete config.toml template matching frontend defaults
- Generating safe provider name from sanitized input
- Including model_provider, reasoning_effort, and wire_api settings
- Removing minimal template that only contained base_url
- Cleaning up deprecated test file deeplink-test.html
* style: fix clippy uninlined_format_args warnings
Apply clippy --fix to use inline format arguments in:
- src/mcp.rs (8 fixes)
- src/services/env_manager.rs (10 fixes)
* style: apply code formatting and cleanup
- Format TypeScript files with Prettier (App.tsx, EnvWarningBanner.tsx, formatters.ts)
- Organize Rust imports and module order alphabetically
- Add newline at end of JSON files (en.json, zh.json)
- Update Cargo.lock for dependency changes
* feat: add model name configuration support for Codex and fix Gemini model handling
- Add visual model name input field for Codex providers
- Add model name extraction and update utilities in providerConfigUtils
- Implement model name state management in useCodexConfigState hook
- Add conditional model field rendering in CodexFormFields (non-official only)
- Integrate model name sync with TOML config in ProviderForm
- Fix Gemini deeplink model injection bug
- Correct environment variable name from GOOGLE_GEMINI_MODEL to GEMINI_MODEL
- Add test cases for Gemini model injection (with/without model)
- All tests passing (9/9)
- Fix Gemini model field binding in edit mode
- Add geminiModel state to useGeminiConfigState hook
- Extract model value during initialization and reset
- Sync model field with geminiEnv state to prevent data loss on submit
- Fix missing model value display when editing Gemini providers
Changes:
- 6 files changed, 245 insertions(+), 13 deletions(-)
BREAKING CHANGE: The [mcp.servers] format was completely incorrect and not
any official Codex format. The only correct format is [mcp_servers] at the
top level of config.toml.
Changes:
- Remove incorrect [mcp.servers] nested table support
- Always use [mcp_servers] top-level table (official Codex format)
- Auto-migrate and cleanup erroneous [mcp.servers] entries on write
- Preserve error-tolerant import for migrating old incorrect configs
- Simplify sync logic by removing format selection branches (~60 lines)
- Update all documentation and tests to reflect correct format
- Add warning logs when detecting and cleaning incorrect format
Backend (Rust):
- mcp.rs: Simplify sync_enabled_to_codex by removing Target enum
- mcp.rs: sync_single_server_to_codex now always uses [mcp_servers]
- mcp.rs: remove_server_from_codex cleans both locations
- mcp.rs: Update import_from_codex comments to clarify format status
- tests: Rename test to sync_enabled_to_codex_migrates_erroneous_*
- tests: Update assertions to verify migration behavior
Frontend (TypeScript):
- tomlUtils.ts: Prioritize [mcp_servers] format in parsing
- tomlUtils.ts: Update error messages to guide correct format
Documentation:
- README.md: Correct MCP format reference to [mcp_servers]
- CLAUDE.md: Add comprehensive format specification with examples
All 79 tests pass. This ensures backward compatibility while enforcing
the correct Codex official standard going forward.
Refs: https://github.com/openai/codex/issues/3441
Fixes two critical issues with the MCP JSON input:
1. Format button failed with wrapped format like "server": {...}
2. Submit button failed despite input validation passing
Changes:
- Updated formatJSON to use smart parser (supports wrapped format)
- Simplified submit validation logic (removed redundant validateJsonConfig call)
- Improved UX: input preserves original format, cleanup happens on format/submit
* Prevents confusing "instant disappearance" when pasting wrapped JSON
* Auto-fills ID/Name fields while keeping input unchanged
* Format button now strips wrapper key and formats cleanly
* Submit button correctly extracts config regardless of format
Code quality:
- Reduced code by 5 lines (20 changes, 11 insertions, 16 deletions)
- Consistent use of parseSmartMcpJson across all JSON operations
- No type errors introduced
Support multiple MCP configuration input formats:
- Pure config object: { "command": "npx", ... }
- Key-value pair fragment: "server-name": { "command": "npx", ... }
- Wrapped object: { "server-name": { "command": "npx", ... } }
The parser automatically:
- Detects and wraps JSON fragments into complete objects
- Extracts server name from single-key objects
- Auto-fills ID and Name fields when applicable
- Formats the config for display
This improves UX by allowing users to paste configs directly from
.claude.json or .codex/config.toml without manual editing.
Fixed an issue where custom/extension fields (e.g., timeout_ms, retry_count)
were silently dropped when editing Codex MCP server configurations in TOML format.
Root cause: The TOML parser functions only extracted known fields (type, command,
args, env, cwd, url, headers), discarding any additional fields during normalization.
Changes:
- mcpServerToToml: Now uses spread operator to copy all fields before stringification
- normalizeServerConfig: Added logic to preserve unknown fields after processing known ones
- Both stdio and http server types now retain custom configuration fields
This fix enables forward compatibility with future MCP protocol extensions and
allows users to add custom configurations without code changes.
Implement Solution A (complete deferred submission) for custom endpoint
management, replacing the dual-mode system with unified local staging.
Changes:
- Remove immediate backend saves from EndpointSpeedTest
* handleAddEndpoint: local state update only
* handleRemoveEndpoint: local state update only
* handleSelect: remove lastUsed timestamp update
- Add explicit clear detection in ProviderForm
* Distinguish "user cleared endpoints" from "user didn't modify"
* Pass empty object {} as clear signal vs null for no-change
- Fix mergeProviderMeta to handle three distinct cases:
* null/undefined: don't modify endpoints (no meta sent)
* empty object {}: explicitly clear endpoints (send empty meta)
* with data: add/update endpoints (overwrite)
Fixed Critical Bug:
When users deleted all custom endpoints, changes were not saved because:
- draftCustomEndpoints=[] resulted in customEndpointsToSave=null
- mergeProviderMeta(meta, null) returned undefined
- Backend interpreted missing meta as "don't modify", preserving old values
Solution:
Detect when user had endpoints and cleared them (hadEndpoints && length===0),
then pass empty object to mergeProviderMeta as explicit clear signal.
Architecture Improvements:
- Transaction atomicity: all fields submitted together on form save
- UX consistency: add/edit modes behave identically
- Cancel button: true rollback with no immediate saves
- Code simplification: removed ~40 lines of immediate save error handling
Testing:
- TypeScript type check: passed
- Rust backend tests: 10/10 passed
- Build: successful
Add compatibility for providers that use ANTHROPIC_API_KEY instead of
ANTHROPIC_AUTH_TOKEN, enabling support for AiHubMix and similar services.
Changes:
- Backend: Add fallback to ANTHROPIC_API_KEY in credential extraction
- migration.rs: Support API_KEY during config migration
- services/provider.rs: Extract credentials from either field
- Frontend: Smart API key field detection and management
- getApiKeyFromConfig: Read from either field (AUTH_TOKEN priority)
- setApiKeyInConfig: Write to existing field, preserve user choice
- hasApiKeyField: Detect presence of either field
- Add AiHubMix provider preset with dual endpoint candidates
- Define apiKeyField metadata for provider presets (documentation)
Technical Details:
- Uses or_else() for zero-cost field fallback in Rust
- Runtime field detection ensures correct field name preservation
- Maintains backward compatibility with existing configurations
- Priority: ANTHROPIC_AUTH_TOKEN > ANTHROPIC_API_KEY
- Rename src/config/providerPresets.ts to claudeProviderPresets.ts
- Update all import statements across 11 files to reflect the new name
- Establish symmetrical naming convention with codexProviderPresets.ts
- Improve code clarity and maintainability
- Add postChangeSync.ts utility with Result pattern for graceful error handling
- Replace try-catch with syncCurrentProvidersLiveSafe in useImportExport
- Add directory-change-triggered sync in useSettings to maintain SSOT
- Introduce partial-success status to distinguish import success from sync failures
- Add test coverage for sync behavior in different scenarios
This refactoring ensures config.json changes are reliably synced to live
files while providing better user feedback for edge cases.
Add one-click format functionality for JSON editors with toast notifications:
- Add format button to JsonEditor (CodeMirror)
- Add format button to CodexAuthSection (auth.json)
- Add format button to CommonConfigEditor (settings + modal)
- Add formatJSON utility function
- Add i18n keys: format, formatSuccess, formatError
Note: TOML formatting was intentionally NOT added to avoid losing comments
during parse/stringify operations. TOML validation remains available via
the existing useCodexTomlValidation hook.
Fixed two critical data loss bugs where user-added custom endpoints were discarded:
1. **AddProviderDialog**: Form submission ignored values.meta from ProviderForm and
re-inferred URLs only from presets/config, causing loss of endpoints added via
speed test modal. Now prioritizes form-collected meta and uses fallback inference
only when custom_endpoints is missing.
2. **ProviderForm**: Edit mode always returned initialData.meta, discarding any
changes made in the speed test modal. Now uses mergeProviderMeta to properly
merge customEndpointsMap with existing meta fields.
Changes:
- Extract mergeProviderMeta utility to handle meta field merging logic
- Preserve other meta fields (e.g., usage_script) during endpoint updates
- Unify new/edit code paths to use consistent meta handling
- Add comprehensive unit tests for meta merging scenarios
- Add integration tests for AddProviderDialog submission flow
Impact:
- Third-party and custom providers can now reliably manage multiple endpoints
- Edit operations correctly reflect user modifications
- No data loss for existing meta fields like usage_script
- Separate MCP server metadata from connection spec for cleaner architecture
- Add comprehensive server entry fields: name, description, tags, homepage, docs
- Remove legacy format compatibility logic from extract_server_spec
- Implement data validation and filtering in get_servers_snapshot_for
- Add strict id consistency check in upsert_in_config_for
- Enhance import logic with defensive programming for corrupted data
- Simplify frontend by removing normalization logic (moved to backend)
- Improve error messages with contextual information
- Add comprehensive i18n support for new metadata fields
- chore(deps): switch TOML dependency from @iarna/toml to smol-toml and update lockfile
- feat(mcp): add TOML editing/validation for Codex while keeping JSON for Claude; support auto ID extraction from TOML and JSON->TOML conversion for wizard output; add pre-submit required checks (stdio.command / http.url)
- refactor(mcp): unify JSON/TOML validation errors via i18n; add formatTomlError for consistent, localized messages; consolidate state into formConfig/configError
- feat(i18n): add TOML labels/placeholders and error keys (tomlConfig, tomlPlaceholder, tomlInvalid)
- feat(utils): introduce tomlUtils with parse/stringify/validate/convert helpers using smol-toml; provide tomlToMcpServer, mcpServerToToml, extractIdFromToml, validateToml
- build: confirm Vite no longer externalizes Node builtins during build; renderer builds without 'Module 'stream' has been externalized' warning
* feat: add unified endpoint speed test for API providers
Add a comprehensive endpoint latency testing system that allows users to:
- Test multiple API endpoints concurrently
- Auto-select the fastest endpoint based on latency
- Add/remove custom endpoints dynamically
- View latency results with color-coded indicators
Backend (Rust):
- Implement parallel HTTP HEAD requests with configurable timeout
- Handle various error scenarios (timeout, connection failure, invalid URL)
- Return structured latency data with status codes
Frontend (React):
- Create interactive speed test UI component with auto-sort by latency
- Support endpoint management (add/remove custom endpoints)
- Extract and update Codex base_url from TOML configuration
- Integrate with provider presets for default endpoint candidates
This feature improves user experience when selecting optimal API endpoints,
especially useful for users with multiple provider options or proxy setups.
* refactor: convert endpoint speed test to modal dialog
- Transform EndpointSpeedTest component into a modal dialog
- Add "Advanced" button next to base URL input to open modal
- Support ESC key and backdrop click to close modal
- Apply Linear design principles: minimal styling, clean layout
- Remove unused showBaseUrlInput variable
- Implement same modal pattern for both Claude and Codex
* fix: prevent modal cascade closing when ESC is pressed
- Add state checks to prevent parent modal from closing when child modals (endpoint speed test or template wizard) are open
- Update ESC key handler dependencies to track all modal states
- Ensures only the topmost modal responds to ESC key
* refactor: unify speed test panel UI with project design system
UI improvements:
- Update modal border radius from rounded-lg to rounded-xl
- Unify header padding from px-6 py-4 to p-6
- Change speed test button color to blue theme (bg-blue-500) for consistency
- Update footer background from bg-gray-50 to bg-gray-100
- Style "Done" button as primary action button with blue theme
- Adjust footer button spacing and hover states
Simplify endpoint display:
- Remove endpoint labels (e.g., "Current Address", "Custom 1")
- Display only URL for cleaner interface
- Clean up all label-related logic:
* Remove label field from EndpointCandidate interface
* Remove label generation in buildInitialEntries function
* Remove label handling in useEffect merge logic
* Remove label generation in handleAddEndpoint
* Remove label parameters from claudeSpeedTestEndpoints
* Remove label parameters from codexSpeedTestEndpoints
* refactor: improve endpoint list UI consistency
- Show delete button for all endpoints on hover for uniform UI
- Change selected state to use blue theme matching main interface:
* Blue border (border-blue-500) for selected items
* Light blue background (bg-blue-50/dark:bg-blue-900/20)
* Blue indicator dot (bg-blue-500/dark:bg-blue-400)
- Switch from compact list (space-y-px) to card-based layout (space-y-2)
- Add rounded corners to each endpoint item for better visual separation
* feat: persist custom endpoints to settings.json
- Extend AppSettings to store custom endpoints for Claude and Codex
- Add Tauri commands: get/add/remove/update custom endpoints
- Update frontend API with endpoint persistence methods
- Modify EndpointSpeedTest to load/save custom endpoints via API
- Track endpoint last used time for future sorting/cleanup
- Store endpoints per app type in settings.json instead of localStorage
* - feat(types): add Provider.meta and ProviderMeta (snake_case) with custom_endpoints map
- feat(provider-form): persist custom endpoints on provider create by merging EndpointSpeedTest’s custom URLs into meta.custom_endpoints on submit
- feat(endpoint-speed-test): add onCustomEndpointsChange callback emitting normalized custom URLs; wire it for both Claude/Codex modals
- fix(api): send alias param names (app/appType/app_type and provider_id/providerId) in Tauri invokes to avoid “missing providerId” with older backends
- storage: custom endpoints are stored in ~/.cc-switch/config.json under providers[<id>].meta.custom_endpoints (not in settings.json)
- behavior: edit flow remains immediate writes; create flow now writes once via addProvider, removing the providerId dependency during creation
* feat: add endpoint candidates support and code formatting improvements
- Add endpointCandidates field to ProviderPreset and CodexProviderPreset interfaces
- Integrate preset endpoint candidates into speed test endpoint selection
- Add multiple endpoint options for PackyCode providers (Claude & Codex)
- Apply consistent code formatting (trailing commas, line breaks)
- Improve template value type safety and readability
* refactor: improve endpoint management button UX
Replace ambiguous "Advanced" text with intuitive "Manage & Test" label accompanied by Zap icon, making the endpoint management panel entry point more discoverable and self-explanatory for both Claude and Codex configurations.
* - merge: merge origin/main, resolve conflicts and preserve both feature sets
- feat(tauri): register import/export and file dialogs; keep endpoint speed test and custom endpoints
- feat(api): add updateTrayMenu and onProviderSwitched; wire import/export APIs
- feat(types): extend global API declarations (import/export)
- chore(presets): GLM preset supports both new and legacy model keys
- chore(rust): add chrono dependency; refresh lockfile
---------
Co-authored-by: Jason <farion1231@gmail.com>
Merged duplicate try-catch blocks that handle invalid chatgpt.config values.
Now handles all edge cases (scalar values, arrays, empty objects) in a single
unified check, reducing code duplication and improving performance by parsing
JSON only once.
- refactor(ProviderList): use shared base_url helpers
- refactor(App): reuse shared base_url helpers for VS Code sync
- fix(auto-sync): global shared VS Code auto-apply state (localStorage + event broadcast)
- feat(tray): auto-apply to VS Code on Codex provider-switched when enabled
- behavior: manual Apply enables auto-sync; manual Remove disables; official providers clear managed keys only
- chore(typecheck): pass pnpm typecheck
- feat(tauri): Add commands to read/write VS Code settings.json with cross-variant detection (Code/Insiders/VSCodium/OSS)
- fix(vscode): Use top-level keys “chatgpt.apiBase” and “chatgpt.config.preferred_auth_method”
- fix(vscode): Handle empty settings.json (skip deletes, direct write) to avoid “Can not delete in empty document”
- fix(windows): Make atomic writes robust by removing target before rename
- ui(provider-list): Improve error surfacing when applying/removing
- chore(types): Extend window.api typings and tauri-api wrappers for VS Code commands
- deps: Add jsonc-parser
- Add automatic VS Code config sync when base_url changes in TOML
- Improve error handling for VS Code configuration writes
- Enhance state management with ref tracking to prevent duplicate API calls
- Fix code formatting issues and improve readability across components
- Optimize common configuration handling for both Claude and Codex providers
- Extract common validateJsonConfig function for reuse
- Apply unified validation to both main config and common config snippets
- Add real-time JSON validation to JsonEditor component using CodeMirror linter
- Simplify error handling without over-engineering error position extraction
- Move extractErrorMessage function from App.tsx to utils/errorUtils.ts
- Improve code organization and reusability
- Enhance error notification with dynamic message extraction and timeout
- Added separate common config state and storage for Codex
- Implemented TOML-based common config merging with markers
- Created UI components for Codex common config editor
- Added toggle and edit functionality similar to Claude config
- Store Codex common config in localStorage separately
- Support appending/removing common TOML snippets to config.toml
- Replace single "disable co-authored" checkbox with universal "common config snippet" functionality
- Add localStorage persistence for common config snippets
- Implement deep merge/remove operations for complex JSON structures
- Add modal editor for managing common config snippets
- Optimize performance with custom deepClone function instead of JSON.parse/stringify
- Fix deep remove logic to only delete matching values
- Improve error handling and validation for JSON snippets
* feat: refactor ProviderForm component with new subcomponents
- Introduced PresetSelector, ApiKeyInput, ClaudeConfigEditor, and CodexConfigEditor for improved modularity and readability.
- Simplified preset selection logic for both Claude and Codex configurations.
- Enhanced API Key input handling with dedicated components for better user experience.
- Removed redundant code and improved state management in the ProviderForm component.
* feat: add Kimi model selection to ProviderForm component
- Introduced KimiModelSelector for enhanced model configuration options.
- Implemented state management for Kimi model selection, including initialization and updates based on preset selection.
- Improved user experience by conditionally displaying the Kimi model selector based on the selected preset.
- Refactored related logic to ensure proper handling of Kimi-specific settings in the ProviderForm.
* feat: enhance API Key input and model selection in ProviderForm
- Added toggle functionality to show/hide API Key in ApiKeyInput component for improved user experience.
- Updated placeholder text in ProviderForm to provide clearer instructions based on the selected preset.
- Enhanced KimiModelSelector to display a more informative message when API Key is not provided.
- Refactored provider presets to remove hardcoded API Key values for better security practices.
* fix(kimi): optimize debounce implementation in model selector
- Fix initial state: use empty string instead of apiKey.trim()
- Refactor fetchModels to fetchModelsWithKey with explicit key parameter
- Ensure consistent behavior between auto-fetch and manual refresh
- Eliminate mental overhead from optional parameter fallback logic
* fix(api-key): remove custom masking logic, use native password input
- Remove getDisplayValue function with custom star masking
- Use native browser password input behavior for better UX consistency
- Simplify component logic while maintaining show/hide toggle functionality
* chore: format code with prettier
- Apply consistent code formatting across all TypeScript files
- Fix indentation and spacing according to project style guide
---------
Co-authored-by: Jason <farion1231@gmail.com>
* feat(editor): add JsonEditor component for JSON configuration editing
* fix(provider): update API Key visibility logic in ProviderForm component
* fix(editor): stabilize JsonEditor height and restore API Key logic
- Revert API Key visibility to preset-based rule and injection to non-custom preset only.
- Reduce JsonEditor min-height from rows*22px to rows*18px to lessen layout jitter when switching presets.
- Keep fonts/size consistent with the previous textarea for visual parity.
* - fix(form): remove websiteUrl auto-extraction from JSON to prevent incorrect overrides
---------
Co-authored-by: Jason <farion1231@gmail.com>
- Format all TypeScript/React code with Prettier
- Format all Rust code with cargo fmt
- Fix bundle identifier from .app to .desktop to avoid macOS conflicts
- Prepare codebase for v3.0.0 Tauri release