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
Rename `AppType` to `AppId` across the entire frontend codebase to better
reflect its purpose as an application identifier rather than a type category.
This aligns frontend naming with backend command parameter conventions.
Changes:
- Rename type `AppType` to `AppId` in src/lib/api/types.ts
- Remove `AppType` export from src/lib/api/index.ts
- Update all component props from `appType` to `appId` (43 files)
- Update all variable names from `appType` to `appId`
- Synchronize documentation (CHANGELOG, refactoring plans)
- Update test files and MSW mocks
BREAKING CHANGE: `AppType` type is no longer exported. Use `AppId` instead.
All component props have been renamed from `appType` to `appId`.
- Migrate all form components from ProviderForm/ to providers/forms/
- Create shared components to eliminate code duplication:
* ApiKeySection: unified API key input with "Get API Key" link
* EndpointField: unified endpoint URL input with manage button
- Refactor ClaudeFormFields (-31% lines) and CodexFormFields (-33% lines)
- Update all import paths to use new locations
- Reduce code duplication from ~12% to ~7%
This change improves maintainability and makes the codebase more DRY.
- Create useSpeedTestEndpoints hook that collects endpoints from:
1. Current baseUrl/codexBaseUrl
2. Initial data URL (edit mode)
3. Preset's endpointCandidates array
- Pass speedTestEndpoints to ClaudeFormFields and CodexFormFields
- Update EndpointSpeedTest to use collected endpoints instead of just current URL
- Fix PackyCode preset endpoints not appearing in speed test modal
- Created ProviderPresetSelector component (80 lines)
- Created BasicFormFields component (60 lines)
- Created ClaudeFormFields component (272 lines)
- Created CodexFormFields component (131 lines)
- Reduced ProviderForm from 866 to 544 lines (37% reduction)
Each component now has a clear single responsibility:
- ProviderPresetSelector: Handles preset selection UI
- BasicFormFields: Name and website URL inputs
- ClaudeFormFields: All Claude-specific form fields
- CodexFormFields: All Codex-specific form fields
- ProviderForm: Orchestrates hooks and component composition
Benefits:
- Better code organization and maintainability
- Easier to test individual components
- Clearer separation of concerns
- More reusable components