593 Commits

  • feat(presets): add Kimi For Coding and BaiLing provider presets
    Add two new Chinese official provider presets:
    - Kimi For Coding: AI coding assistant from Kimi
    - BaiLing: Claude-compatible API from AliPay TBox
  • docs: add TypeScript Trending badge and improve contributing tone
    - Add 🔥 TypeScript Trending badge celebrating daily/weekly/monthly rankings
    - Refine contributing guidelines with friendlier, more welcoming language
    - Replace directive tone with collaborative suggestions for feature PRs
  • chore(release): prepare for v3.6.1 release
    - Bump version to 3.6.1 across all config files
      - package.json, Cargo.toml, tauri.conf.json
      - README.md and README_ZH.md version badges
      - Auto-updated Cargo.lock
    - Add release notes documentation
      - docs/release-note-v3.6.0-en.md (archive)
      - docs/release-note-v3.6.1-zh.md (cumulative format)
      - docs/release-note-v3.6.1-en.md (cumulative format)
    - Enlarge PackyCode sponsor logo by 50% (100px → 150px)
    - Update roadmap checklist (homebrew support marked as done)
  • feat(sponsor): add PackyCode as official partner
    - Add PackyCode to sponsor section in README (both EN/ZH)
    - Add PackyCode logo to assets/partners/logos/
    - Mark PackyCode as partner in provider presets (Claude & Codex)
    - Add promotion message to i18n files with 10% discount info
  • refactor(usage-script): replace native checkbox with Switch component
    Upgrade the enable toggle from native checkbox to shadcn/ui Switch component
    for better UX and UI consistency with settings page.
    
    **Improvements**:
    1. Use modern toggle UI (Switch) instead of traditional checkbox
    2. Adopt the same layout pattern as settings page (ToggleRow style)
    3. Add bordered container with proper spacing for better visual hierarchy
    4. Maintain full accessibility support (aria-label)
    
    **Layout changes**:
    - Before: Simple label + checkbox horizontal layout
    - After: Bordered container, label on left, Switch on right, vertically centered
  • fix(usage-script): replace FormLabel with Label to fix white screen crash
    FormLabel component requires FormField context and throws error when used
    standalone, causing the entire component to crash with a white screen.
    
    Root cause:
    - FormLabel internally calls useFormField() hook
    - useFormField() requires FormFieldContext (must be within <FormField>)
    - Without context, it throws: "useFormField should be used within <FormField>"
    - Uncaught error crashes React rendering tree
    
    Solution:
    - Replace FormLabel with standalone Label component
    - Label component from @/components/ui/label doesn't depend on form context
    - Maintains same styling (text-sm font-medium) without requiring context
    
    This fixes the white screen issue when clicking the usage panel.
  • style(usage-script): unify form input styles with shadcn/ui components
    Replace native HTML input elements with shadcn/ui Input and FormLabel
    components to ensure consistent styling across the application.
    
    Changes:
    - Import Input, FormLabel, Eye, and EyeOff components
    - Replace all credential input fields with Input component
    - Add show/hide toggle buttons for password fields (API Key, Access Token)
    - Replace label/span elements with FormLabel component
    - Update timeout and auto-query interval inputs to use Input component
    - Improve spacing consistency (space-y-4 for credential config)
    - Add proper id attributes for accessibility
    - Use muted-foreground for hint text
    
    The form now matches the styling of provider configuration forms
    throughout the application.
  • feat(usage-query): decouple credentials from provider config
    Add independent credential fields for usage query to support different
    query endpoints and authentication methods.
    
    Changes:
    - Add `apiKey` and `baseUrl` fields to UsageScript struct
    - Remove dependency on provider config credentials in query_usage
    - Update test_usage_script to accept independent credential parameters
    - Add credential input fields in UsageScriptModal based on template:
      * General: apiKey + baseUrl
      * NewAPI: baseUrl + accessToken + userId
      * Custom: no additional fields (full freedom)
    - Auto-clear irrelevant fields when switching templates
    - Add i18n text for "credentialsConfig"
    
    Benefits:
    - Query API can use different endpoint/key than provider config
    - Better separation of concerns
    - More flexible for various usage query scenarios
  • fix(toml): normalize CJK quotes to prevent parsing errors
    Add quote normalization to handle Chinese/fullwidth quotes automatically
    converted by IME. This fixes TOML parsing failures when users input
    configuration with non-ASCII quotes (" " ' ' etc.).
    
    Changes:
    - Add textNormalization utility for quote normalization
    - Apply normalization in TOML input handlers (MCP form, Codex config)
    - Disable browser auto-correction in Textarea component
    - Add defensive normalization in TOML parsing layer
  • fix(mcp): preserve custom fields in Codex TOML config editor
    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.
  • feat(schema): add common JSON/TOML validators and enforce MCP conditional fields
    - Add src/lib/schemas/common.ts with jsonConfigSchema and tomlConfigSchema
    - Enhance src/lib/schemas/mcp.ts to require command for stdio and url for http via superRefine
    - Keep ProviderForm as-is; future steps will wire new schemas into RHF flows
    - Verified: pnpm typecheck passes
  • feat(ui): enhance provider switch error notification with copy action
    - Split error message into title and description for better UX
    - Add one-click copy button to easily share error details
    - Extend toast duration to 6s for improved readability
    - Use extractErrorMessage utility for consistent error handling
    - Add i18n keys: common.copy, notifications.switchFailedTitle
    
    This improves debugging experience when provider switching fails,
    allowing users to quickly copy and share error messages.
  • fix(tray): replace unwrap with safe pattern matching in menu handler
    Replace unwrap() calls with safe pattern matching to prevent panics
    when handling invalid tray menu item IDs. Now logs errors and returns
    gracefully instead of crashing the application.
  • fix(provider): set category to custom for imported default config
    Ensure that when importing default configuration from live files,
    the created provider is properly marked with category "custom" for
    correct UI display and filtering.
  • fix(error-handling): isolate tray menu update failures from main operations
    Previously, if updateTrayMenu() failed after a successful main operation
    (like sorting, adding, or updating providers), the entire operation would
    appear to fail with a misleading error message, even though the core
    functionality had already succeeded.
    
    This resulted in false negative feedback where:
    - Backend data was successfully updated
    - Frontend UI was successfully refreshed
    - Tray menu failed to update
    - User saw "operation failed" message (incorrect)
    
    Changes:
    - Wrap updateTrayMenu() calls in nested try-catch blocks
    - Log tray menu failures separately with descriptive messages
    - Ensure main operation success is reported accurately
    - Prevent tray menu failures from triggering main operation error handlers
    
    Files modified:
    - src/hooks/useDragSort.ts (drag-and-drop sorting)
    - src/lib/query/mutations.ts (add/delete/switch mutations)
    - src/hooks/useProviderActions.ts (update provider)
    
    This fixes the bug introduced in PR #179 and prevents similar issues
    across all provider operations.
  • fix(ui): sync tray menu order after drag-and-drop sorting (#179)
    The drag-and-drop sorting feature introduced in PR #126 (9eb991d) was
    updating the provider sort order in the backend and frontend, but failed
    to update the tray menu to reflect the new order.
    
    Changes:
    - Add updateTrayMenu() call after successful sort order update
    - Ensures tray menu items are immediately reordered to match the UI
    
    This fixes the issue where dragging providers in the main window would
    not update their order in the system tray menu.
    
    Fixes: 9eb991d (feat(ui): add drag-and-drop sorting for provider list)
  • fix(forms): populate base URL for all non-official provider categories
    The base URL field was not populating when editing providers with
    categories like cn_official or aggregator. The issue was caused by
    inconsistent conditional logic: the input field was shown for all
    non-official categories, but the value extraction only worked for
    third_party and custom categories.
    
    Changed the category check from allowlist (third_party, custom) to
    denylist (official) to match the UI display logic. Now ANTHROPIC_BASE_URL
    correctly populates for all provider categories except official.
  • fix(ui): remove misleading model placeholders from input fields
    Clear placeholder values for model input fields to avoid suggesting specific model names that may not be applicable to all providers. This prevents user confusion when configuring different Claude providers.
  • feat(usage): enable background query for usage polling
    - Add `refetchIntervalInBackground: true` to usage query configuration
    - Allows usage queries to continue running when app window is minimized or unfocused
    - Existing safety mechanisms remain in place (timeout limits, minimum interval, no retry)
    - Users have full control through autoQueryInterval setting
  • docs: add v3.6.0 release notes and update READMEs with missing features
    **Release Notes**
    - Create Chinese v3.6.0 release notes in docs/ folder
    
    **README Updates**
    Add documentation for three missing v3.6.0 features:
    
    1. Claude Configuration Data Structure Enhancements
       - Granular model configuration: migrated from dual-key to quad-key system
       - New fields: ANTHROPIC_DEFAULT_HAIKU_MODEL, ANTHROPIC_DEFAULT_SONNET_MODEL,
         ANTHROPIC_DEFAULT_OPUS_MODEL, ANTHROPIC_MODEL
       - Replaces legacy ANTHROPIC_SMALL_FAST_MODEL with automatic migration
       - Backend normalizes old configs with smart fallback chain
       - UI expanded from 2 to 4 model input fields
    
    2. Updated Provider Models
       - Kimi: updated to latest kimi-k2-thinking model (from k1 series)
       - Removed legacy KimiModelSelector component
    
    3. Custom Configuration Directory (Cloud Sync Support)
       - Customize CC Switch's configuration storage location
       - Point to cloud sync folders (Dropbox, OneDrive, iCloud, etc.)
         to enable automatic config synchronization across devices
       - Managed via Tauri Store for better isolation
    
    **Files Changed**
    - README.md & README_ZH.md: added feature documentation
    - docs/release-note-v3.6.0-zh.md: comprehensive Chinese release notes
  • Release v3.6.0: Major architecture refactoring and feature enhancements
    New Features:
    - Provider duplication and manual sorting via drag-and-drop
    - Custom endpoint management for aggregator providers
    - Usage query with auto-refresh interval and test script API
    - Config editor improvements (JSON format button, real-time TOML validation)
    - Auto-sync on directory change for WSL environment support
    - Load live config when editing active provider to protect manual modifications
    - New provider presets: DMXAPI, Azure Codex, AnyRouter, AiHubMix, MiniMax
    - Partner promotion mechanism (Zhipu GLM Z.ai)
    
    Architecture Improvements:
    - Backend: 5-phase refactoring (error handling → command split → services → concurrency)
    - Frontend: 4-stage refactoring (tests → hooks → components → cleanup)
    - Testing: 100% hooks unit test coverage, integration tests for critical flows
    
    Documentation:
    - Complete README rewrite with detailed architecture overview
    - Separate Chinese (README_ZH.md) and English (README.md) versions
    - Comprehensive v3.6.0 changelog with categorized changes
    - New bilingual screenshots and partner banners
    
    Bug Fixes:
    - Fixed configuration sync issues (apiKeyUrl priority, MCP sync, import sync)
    - Fixed usage query interval timing and refresh button animation
    - Fixed UI issues (edit mode alignment, language switch state)
    - Fixed endpoint speed test and provider duplicate insertion position
    - Force exit on config error to prevent silent fallback
    
    Technical Details:
    - Updated to Tauri 2.8.x, TailwindCSS 4.x, TanStack Query v5.90.x
    - Removed legacy v1 migration logic for better startup performance
    - Standardized command parameters (unified to camelCase `app`)
    - Result pattern for graceful error handling
  • fix(forms): show endpoint input for all non-official providers
    Previously, endpoint URL input was only shown for aggregator, third_party,
    and custom categories. This excluded cn_official providers from managing
    custom endpoints.
    
    Changed logic to show endpoint input for all categories except official,
    which fixes the issue and simplifies the condition.
  • refactor(settings): improve directory configuration UI layout
    - Rebrand "CC-Switch" to "CC Switch" across all UI text
    - Separate CC Switch config directory into standalone section at top
    - Update description to highlight cloud sync capability
    - Remove redundant descriptions for Claude/Codex directory inputs
    - Improve Chinese grammar for WSL configuration description
  • chore: apply cargo fmt before release
    - Format code in app_config.rs to comply with rustfmt rules
    - Remove extra blank line in config.rs
    - Format test code in app_config_load.rs for consistency
  • refactor(cleanup): remove dead code and optimize Option checks
    - Remove 5 unused functions that were left over from migration refactoring:
      - get_archive_root, ensure_unique_path, archive_file (config.rs)
      - read_config_text_from_path, read_and_validate_config_from_path (codex_config.rs)
    - Simplify is_v1 check using is_some_and instead of map_or for better readability
    - Remove outdated comments about removed functions
    
    This eliminates all dead_code warnings and improves code maintainability.
  • feat: add Z.ai GLM partner and fix apiKeyUrl priority
    - Add Z.ai GLM as official partner with promotion support
    - Fix apiKeyUrl not being prioritized for cn_official and aggregator categories
    - Now apiKeyUrl (with promotion parameters) takes precedence over websiteUrl for cn_official, aggregator, and third_party categories
  • feat: add partner promotion feature for Zhipu GLM
    - Add isPartner and partnerPromotionKey fields to Provider and ProviderPreset types
    - Display gold star badge on partner presets in selector
    - Show promotional message in API Key section for partners
    - Configure Zhipu GLM as official partner with 10% discount promotion
    - Support both Claude and Codex provider presets
    - Add i18n support for partner promotion messages (zh/en)
  • docs(changelog): document breaking changes in [Unreleased]
    Add comprehensive documentation for three breaking changes:
    1. Runtime auto-migration from v1 to v2 config format removed
    2. Legacy v1 copy file migration logic removed
    3. Tauri commands now only accept 'app' parameter
    
    Also document improvements and new tests added in recent commits.
  • refactor(migration): remove legacy v1 copy file migration logic
    Remove the entire migration module (435 lines) that was used for
    one-time migration from v3.1.0 to v3.2.0. This cleans up technical
    debt and improves startup performance.
    
    Changes:
    - Delete src-tauri/src/migration.rs (copy file scanning and merging)
    - Remove migration module reference from lib.rs
    - Simplify startup logic to only ensure config structure exists
    - Remove automatic deduplication and archiving logic
    
    BREAKING CHANGE: Users upgrading from v3.1.0 must first upgrade to
    v3.2.x to automatically migrate their configurations.
  • refactor(config): remove v1 auto-migration and improve error handling
    BREAKING CHANGE: Runtime auto-migration from v1 to v2 config format has been removed.
    
    Changes:
    - Remove automatic v1→v2 migration logic from MultiAppConfig::load()
    - Improve v1 detection using structural analysis (checks for 'apps' key absence)
    - Return clear error with migration instructions when v1 config is detected
    - Add comprehensive tests for config loading edge cases
    - Fix false positive detection when v1 config contains 'version' or 'mcp' fields
    
    Migration path for users:
    1. Install v3.2.x to perform one-time auto-migration, OR
    2. Manually edit ~/.cc-switch/config.json to v2 format
    
    Rationale:
    - Separates concerns: load() should be read-only, not perform side effects
    - Fail-fast principle: unsupported formats should error immediately
    - Simplifies code maintenance by removing migration logic from hot path
    
    Tests added:
    - load_v1_config_returns_error_and_does_not_write
    - load_v1_with_extra_version_still_treated_as_v1
    - load_invalid_json_returns_parse_error_and_does_not_write
    - load_valid_v2_config_succeeds
  • chore: unify code formatting and remove unused code
    - Apply cargo fmt to Rust code with multiline error handling
    - Apply Prettier formatting to TypeScript code with trailing commas
    - Unify #[allow(non_snake_case)] attribute formatting
    - Remove unused ProviderNotFound error variant from error.rs
    - Add vitest-report.json to .gitignore to exclude test artifacts
    - Optimize readability of error handling chains with vertical alignment
    
    All tests passing: 22 Rust tests + 126 frontend tests
  • feat(ui): move usage display inline next to enable button
    - Refactor UsageFooter to support inline mode with two-row layout
      - Row 1: Last refresh time + refresh button (right-aligned)
      - Row 2: Used + Remaining + Unit
    - Update ProviderCard layout to show usage inline instead of below
    - Improve text spacing: reduce gap from 1 to 0.5 for tighter label-value pairs
    - Update Chinese translation: "使用" → "已使用" for better clarity
    - Maintain backward compatibility with bottom display mode
    
    This change unifies card heights and improves visual consistency
    across providers with and without usage queries enabled.
  • fix(usage): resolve auto-query interval timing issue
    Problem:
    - User configured 5-minute auto-query interval
    - Actual queries executed every 10 minutes instead of 5
    
    Root Cause:
    - staleTime (5 min) conflicted with refetchInterval (5 min)
    - React Query skipped refetch when data was still within staleTime
    - First interval trigger at T+5min: data considered "fresh", skipped
    - Second interval trigger at T+10min: data "stale", executed
    
    Solution:
    - Set staleTime to 0 for usage queries
    - Ensures refetchInterval executes precisely as configured
    - Auto-query is meant to fetch fresh data periodically, not use cache
    
    Technical Details:
    - Modified useUsageQuery in src/lib/query/queries.ts
    - Changed: staleTime: 5 * 60 * 1000 → staleTime: 0
    - Added explanatory comment in Chinese
    - Manual queries still work via refetch() button
  • refactor(usage): consolidate query logic to eliminate DRY violations
    Breaking Changes:
    - Removed useAutoUsageQuery hook (119 lines)
    - Unified all usage queries into single useUsageQuery hook
    
    Technical Improvements:
    - Eliminated duplicate state management (React Query + manual useState)
    - Fixed single source of truth principle violation
    - Replaced manual setInterval with React Query's built-in refetchInterval
    - Reduced UsageFooter complexity by 28% (54 → 39 lines)
    
    New Features:
    - useUsageQuery now accepts autoQueryInterval option
    - Automatic query interval control (0 = disabled, min 1 minute)
    - Built-in lastQueriedAt timestamp from dataUpdatedAt
    - Auto-query only enabled for currently active provider
    
    Architecture Benefits:
    - Single data source: manual and auto queries share same cache
    - No more state inconsistency between manual/auto query results
    - Leverages React Query's caching, deduplication, and background updates
    - Cleaner separation of concerns
    
    Code Changes:
    - src/lib/query/queries.ts: Enhanced useUsageQuery with auto-query support
    - src/components/UsageFooter.tsx: Simplified to use single query hook
    - src/hooks/useAutoUsageQuery.ts: Deleted (redundant)
    - All type checks passed
  • feat(usage): add auto-refresh interval for usage queries
    New Features:
    - Users can configure auto-query interval in "Configure Usage Query" dialog
    - Interval in minutes (0 = disabled, recommend 5-60 minutes)
    - Auto-query only enabled for currently active provider
    - Display last query timestamp in relative time format (e.g., "5 min ago")
    - Execute first query immediately when enabled, then repeat at intervals
    
    Technical Implementation:
    - Backend: Add auto_query_interval field to UsageScript struct
    - Frontend: Create useAutoUsageQuery Hook to manage timers and query state
    - UI: Add auto-query interval input field in UsageScriptModal
    - Integration: Display auto-query results and timestamp in UsageFooter
    - i18n: Add Chinese and English translations
    
    UX Improvements:
    - Minimum interval protection (1 minute) to prevent API abuse
    - Auto-cleanup timers on component unmount
    - Silent failure handling for auto-queries, non-intrusive to users
    - Prioritize auto-query results, fallback to manual query results
    - Timestamp display positioned next to refresh button for better clarity
  • feat(providers): add DMXAPI provider and refine naming
    - Add DMXAPI provider for both Claude and Codex
    - Rename "Codex Official" to "OpenAI Official" for clarity
    - Rename "Azure OpenAI (gpt-5-codex)" to "Azure OpenAI"
    - Reorganize AiHubMix position in Claude presets
  • refactor(i18n): complete error message internationalization and code cleanup
    Replaced all remaining hardcoded error types with AppError::localized for
    full bilingual support and simplified error handling logic.
    
    Backend changes:
    - usage_script.rs: Converted InvalidHttpMethod to localized error
    - provider.rs: Replaced all 7 ProviderNotFound instances with localized errors
      * Line 436: Delete provider validation
      * Line 625: Update provider metadata
      * Line 785: Test usage script provider lookup
      * Line 855: Query usage provider lookup
      * Line 924: Prepare Codex provider switch
      * Line 1011: Prepare Claude provider switch
      * Line 1272: Delete provider snapshot
    - provider.rs: Simplified error message formatting (removed 40+ lines)
      * Removed redundant string matching fallback logic
      * Now uses clean language-based selection for Localized errors
      * Falls back to default Display for other error types
    - error.rs: Removed unused error variants
      * Deleted InvalidHttpMethod (replaced with localized)
      * Deleted ProviderNotFound (replaced with localized)
    
    Code quality improvements:
    - Reduced complexity: 40+ lines of string matching removed
    - Better maintainability: Centralized error message handling
    - Type safety: All provider errors now use consistent localized format
    
    Impact:
    - 100% i18n coverage for provider and usage script error messages
    - Cleaner, more maintainable error handling code
    - No unused error variants remaining
  • fix(i18n): internationalize test script related error messages
    Fixed 5 hardcoded Chinese error messages to support bilingual display:
    
    Backend changes (services):
    - provider.rs: Fixed 4 error messages:
      * Data format error when deserializing array (line 731)
      * Data format error when deserializing single object (line 738)
      * Regex initialization failure (line 1163)
      * App type not found (line 1191)
    - speedtest.rs: Fixed 1 error message:
      * HTTP client creation failure (line 104)
    
    All errors now use AppError::localized to provide both Chinese and English messages.
    
    Impact:
    - Users will now see properly localized error messages when testing usage scripts
    - Error messages respect the application language setting
    - Better user experience for English-speaking users
  • feat(i18n): complete internationalization for usage query feature
    Fully internationalized the usage query feature to support both Chinese and English.
    
    Frontend changes:
    - Refactored UsageScriptModal to use i18n translation keys
    - Replaced hardcoded Chinese template names with constants
    - Implemented dynamic template generation with i18n support
    - Internationalized all labels, placeholders, and code comments
    - Added template name mapping for translation
    
    Backend changes:
    - Replaced all hardcoded Chinese error messages in usage_script.rs
    - Converted 33 error instances from AppError::Message to AppError::localized
    - Added bilingual error messages for runtime, parsing, HTTP, and validation errors
    
    Translation updates:
    - Added 11 new translation key pairs to zh.json and en.json
    - Covered template names, field labels, placeholders, and code comments
    
    Impact:
    - 100% i18n coverage for usage query functionality
    - All user-facing text and error messages now support language switching
    - Better user experience for English-speaking users
  • feat(usage): add test script API with refactored execution logic
    - Add private helper method `execute_and_format_usage_result` to eliminate code duplication
    - Refactor `query_usage` to use helper method instead of duplicating result processing
    - Add new `test_usage_script` method to test temporary script without saving
    - Add backend command `test_usage_script` accepting script content as parameter
    - Register new command in lib.rs invoke_handler
    - Add frontend `usageApi.testScript` method to call the new backend API
    - Update `UsageScriptModal.handleTest` to test current editor content instead of saved script
    - Improve DX: users can now test script changes before saving
  • feat(usage): add selected state styling to template buttons
    - Add visual feedback for currently selected template
    - Use blue background and white text for selected button
    - Apply gray styling with hover effect for unselected buttons
    - Support dark mode with appropriate color variants
    - Match the same styling pattern used in provider preset selector
  • feat(usage): add custom blank template for usage script
    - Add "自定义" (Custom) template as the first preset option
    - Provide minimal structure with empty URL and headers for full customization
    - Include basic extractor function returning remaining and unit fields
    - Allow users to build usage queries from scratch without starting from complex examples
  • feat(usage): conditionally show advanced config for NewAPI template
    - Add template tracking state to monitor which preset is selected
    - Move advanced config (Access Token & User ID) before script editor for better visibility
    - Show advanced config only when NewAPI template is selected
    - Auto-detect NewAPI template on modal open if accessToken/userId exist
    - Clear advanced fields when switching from NewAPI to other templates
    - Improve UX by placing critical config fields at the top
  • refactor(commands): remove unused missing_param helper function
    Remove dead code that was flagged by Rust compiler. Error handling is now unified through the AppError enum.
  • refactor(endpoints): implement deferred submission and fix clear-all bug
    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
  • feat(usage): add support for access token and user ID in usage scripts
    Add optional accessToken and userId fields to usage query scripts,
    enabling queries to authenticated endpoints like /api/user/self.
    
    Changes:
    - Add accessToken and userId fields to UsageScript type (frontend & backend)
    - Extend script engine to support {{accessToken}} and {{userId}} placeholders
    - Update NewAPI preset template to use /api/user/self endpoint
    - Add UI inputs for access token and user ID in UsageScriptModal
    - Pass new parameters through service layer to script executor
    
    This allows users to query usage data from endpoints that require
    login credentials, providing more accurate balance information for
    services like NewAPI/OneAPI platforms.
  • fix(usage): ensure refresh button shows loading animation on click
    Changed from isLoading to isFetching to properly track all query states.
    Previously, the refresh button spinner would not animate when clicking
    refresh because isLoading only indicates initial load without cached data.
    isFetching covers all query states including manual refetches.
  • fix: resolve name collision in get_init_error command
    The Tauri command `get_init_error` was importing a function with the
    same name from `init_status` module, causing a compile-time error:
    "the name `get_init_error` is defined multiple times".
    
    Changes:
    - Remove `get_init_error` from the use statement in misc.rs
    - Use fully qualified path `crate::init_status::get_init_error()`
      in the command implementation to call the underlying function
    
    This eliminates the ambiguity while keeping the public API unchanged.