mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-01-31 17:53:09 +08:00
* feat(components): add reusable full-screen panel components
Add new full-screen panel components to support the UI refactoring:
- FullScreenPanel: Reusable full-screen layout component with header,
content area, and optional footer. Provides consistent layout for
settings, prompts, and other full-screen views.
- PromptFormPanel: Dedicated panel for creating and editing prompts
with markdown preview support. Features real-time validation and
integrated save/cancel actions.
- AgentsPanel: Panel component for managing agent configurations.
Provides a consistent interface for agent CRUD operations.
- RepoManagerPanel: Full-featured repository manager panel for Skills.
Supports repository listing, addition, deletion, and configuration
management with integrated validation.
These components establish the foundation for the upcoming settings
page migration from dialog-based to full-screen layout.
* refactor(settings): migrate from dialog to full-screen page layout
Complete migration of settings from modal dialog to dedicated full-screen
page, improving UX and providing more space for configuration options.
Changes:
- Remove SettingsDialog component (legacy modal-based interface)
- Add SettingsPage component with full-screen layout using FullScreenPanel
- Refactor App.tsx routing to support dedicated settings page
* Add settings route handler
* Update navigation logic from dialog-based to page-based
* Integrate with existing app switcher and provider management
- Update ImportExportSection to work with new page layout
* Improve spacing and layout for better readability
* Enhanced error handling and user feedback
* Better integration with page-level actions
- Enhance useSettings hook to support page-based workflow
* Add navigation state management
* Improve settings persistence logic
* Better error boundary handling
Benefits:
- More intuitive navigation with dedicated settings page
- Better use of screen space for complex configurations
- Improved accessibility with clearer visual hierarchy
- Consistent with modern desktop application patterns
- Easier to extend with new settings sections
This change is part of the larger UI refactoring initiative to modernize
the application interface and improve user experience.
* refactor(forms): simplify and modernize form components
Comprehensive refactoring of form components to reduce complexity,
improve maintainability, and enhance user experience.
Provider Forms:
- CodexCommonConfigModal & CodexConfigSections
* Simplified state management with reduced boilerplate
* Improved field validation and error handling
* Better layout with consistent spacing
* Enhanced model selection with visual indicators
- GeminiCommonConfigModal & GeminiConfigSections
* Streamlined authentication flow (OAuth vs API Key)
* Cleaner form layout with better grouping
* Improved validation feedback
* Better integration with parent components
- CommonConfigEditor
* Reduced from 178 to 68 lines (-62% complexity)
* Extracted reusable form patterns
* Improved JSON editing with syntax validation
* Better error messages and recovery options
- EndpointSpeedTest
* Complete rewrite for better UX
* Real-time testing progress indicators
* Enhanced error handling with retry logic
* Visual feedback for test results (color-coded latency)
MCP & Prompts:
- McpFormModal
* Simplified from 581 to ~360 lines
* Better stdio/http server type handling
* Improved form validation
* Enhanced multi-app selection (Claude/Codex/Gemini)
- PromptPanel
* Cleaner integration with PromptFormPanel
* Improved list/grid view switching
* Better state management for editing workflows
* Enhanced delete confirmation with safety checks
Code Quality Improvements:
- Reduced total lines by ~251 lines (-24% code reduction)
- Eliminated duplicate validation logic
- Improved TypeScript type safety
- Better component composition and separation of concerns
- Enhanced accessibility with proper ARIA labels
These changes make forms more intuitive, responsive, and easier to
maintain while reducing bundle size and improving runtime performance.
* style(ui): modernize component layouts and visual design
Update UI components with improved layouts, visual hierarchy, and
modern design patterns for better user experience.
Navigation & Brand Components:
- AppSwitcher
* Enhanced visual design with better spacing
* Improved active state indicators
* Smoother transitions and hover effects
* Better mobile responsiveness
- BrandIcons
* Optimized icon rendering performance
* Added support for more provider icons
* Improved SVG handling and fallbacks
* Better scaling across different screen sizes
Editor Components:
- JsonEditor
* Enhanced syntax highlighting
* Better error visualization
* Improved code formatting options
* Added line numbers and code folding support
- UsageScriptModal
* Complete layout overhaul (1239 lines refactored)
* Better script editor integration
* Improved template selection UI
* Enhanced preview and testing panels
* Better error feedback and validation
Provider Components:
- ProviderCard
* Redesigned card layout with modern aesthetics
* Better information density and readability
* Improved action buttons placement
* Enhanced status indicators (active/inactive)
- ProviderList
* Better grid/list view layouts
* Improved drag-and-drop visual feedback
* Enhanced sorting indicators
- ProviderActions
* Streamlined action menu
* Better icon consistency
* Improved tooltips and accessibility
Usage & Footer:
- UsageFooter
* Redesigned footer layout
* Better quota visualization
* Improved refresh controls
* Enhanced error states
Design System Updates:
- dialog.tsx (shadcn/ui component)
* Updated to latest design tokens
* Better overlay animations
* Improved focus management
- index.css
* Added 65 lines of global utility classes
* New animation keyframes
* Enhanced color variables for dark mode
* Improved typography scale
- tailwind.config.js
* Extended theme with new design tokens
* Added custom animations and transitions
* New spacing and sizing utilities
* Enhanced color palette
Visual Improvements:
- Consistent border radius across components
- Unified shadow system for depth perception
- Better color contrast for accessibility (WCAG AA)
- Smoother animations and transitions
- Improved dark mode support
These changes create a more polished, modern interface while
maintaining consistency with the application's design language.
* chore: update dialogs, i18n and improve component integration
Various functional updates and improvements across provider dialogs,
MCP panel, skills page, and internationalization.
Provider Dialogs:
- AddProviderDialog
* Simplified form state management
* Improved preset selection workflow
* Better validation error messages
* Enhanced template variable handling
- EditProviderDialog
* Streamlined edit flow with better state synchronization
* Improved handling of live config backfilling
* Better error recovery for failed updates
* Enhanced integration with parent components
MCP & Skills:
- UnifiedMcpPanel
* Reduced complexity from 140+ to ~95 lines
* Improved multi-app server management
* Better server type detection (stdio/http)
* Enhanced server status indicators
* Cleaner integration with MCP form modal
- SkillsPage
* Simplified navigation and state management
* Better integration with RepoManagerPanel
* Improved error handling for repository operations
* Enhanced loading states
- SkillCard
* Minor layout adjustments
* Better action button placement
Environment & Configuration:
- EnvWarningBanner
* Improved conflict detection messages
* Better visual hierarchy for warnings
* Enhanced dismissal behavior
- tauri.conf.json
* Updated build configuration
* Added new window management options
Internationalization:
- en.json & zh.json
* Added 17 new translation keys for new features
* Updated existing keys for better clarity
* Added translations for new settings page
* Improved consistency across UI text
Code Cleanup:
- mutations.ts
* Removed 14 lines of unused mutation definitions
* Cleaned up deprecated query invalidation logic
* Better type safety for mutation parameters
Overall Impact:
- Reduced total lines by 51 (-10% in affected files)
- Improved component integration and data flow
- Better error handling and user feedback
- Enhanced i18n coverage for new features
These changes improve the overall polish and integration of various
components while removing technical debt and unused code.
* feat(backend): add auto-launch functionality
Implement system auto-launch feature to allow CC-Switch to start
automatically on system boot, improving user convenience.
Backend Implementation:
- auto_launch.rs: New module for auto-launch management
* Cross-platform support using auto-launch crate
* Enable/disable auto-launch with system integration
* Proper error handling for permission issues
* Platform-specific implementations (macOS/Windows/Linux)
Command Layer:
- Add get_auto_launch command to check current status
- Add set_auto_launch command to toggle auto-start
- Integrate commands with settings API
Settings Integration:
- Extend Settings struct with auto_launch field
- Persist auto-launch preference in settings store
- Automatic state synchronization on app startup
Dependencies:
- Add auto-launch ^0.5.0 to Cargo.toml
- Update Cargo.lock with new dependency tree
Technical Details:
- Uses platform-specific auto-launch mechanisms:
* macOS: Login Items via LaunchServices
* Windows: Registry Run key
* Linux: XDG autostart desktop files
- Handles edge cases like permission denials gracefully
- Maintains settings consistency across app restarts
This feature enables users to have CC-Switch readily available
after system boot without manual intervention, particularly useful
for users who frequently switch between API providers.
* refactor(settings): enhance settings page with auto-launch integration
Complete refactoring of settings page architecture to integrate auto-launch
feature and improve overall settings management workflow.
SettingsPage Component:
- Integrate auto-launch toggle with WindowSettings section
- Improve layout and spacing for better visual hierarchy
- Enhanced error handling for settings operations
- Better loading states during settings updates
- Improved accessibility with proper ARIA labels
WindowSettings Component:
- Add auto-launch switch with real-time status
- Integrate with backend auto-launch commands
- Proper error feedback for permission issues
- Visual indicators for current auto-launch state
- Tooltip guidance for auto-launch functionality
useSettings Hook (Major Refactoring):
- Complete rewrite reducing complexity by ~30%
- Better separation of concerns with dedicated handlers
- Improved state management using React Query
- Enhanced auto-launch state synchronization
* Fetch auto-launch status on mount
* Real-time updates on toggle
* Proper error recovery
- Optimized re-renders with better memoization
- Cleaner API for component integration
- Better TypeScript type safety
Settings API:
- Add getAutoLaunch() method
- Add setAutoLaunch(enabled: boolean) method
- Type-safe Tauri command invocations
- Proper error propagation to UI layer
Architecture Improvements:
- Reduced hook complexity from 197 to ~140 effective lines
- Eliminated redundant state management logic
- Better error boundaries and fallback handling
- Improved testability with clearer separation
User Experience Enhancements:
- Instant visual feedback on auto-launch toggle
- Clear error messages for permission issues
- Loading indicators during async operations
- Consistent behavior across all platforms
This refactoring provides a solid foundation for future settings
additions while maintaining code quality and user experience.
* refactor(ui): optimize FullScreenPanel, Dialog and App routing
Comprehensive refactoring of core UI components to improve code quality,
maintainability, and user experience.
FullScreenPanel Component:
- Enhanced props interface with better TypeScript types
- Improved layout flexibility with customizable padding
- Better header/footer composition patterns
- Enhanced scroll behavior for long content
- Added support for custom actions in header
- Improved responsive design for different screen sizes
- Better integration with parent components
- Cleaner prop drilling with context where appropriate
Dialog Component (shadcn/ui):
- Updated to latest component patterns
- Improved animation timing and easing
- Better focus trap management
- Enhanced overlay styling with backdrop blur
- Improved accessibility (ARIA labels, keyboard navigation)
- Better close button positioning and styling
- Enhanced mobile responsiveness
- Cleaner composition with DialogHeader/Footer
App Component Routing:
- Refactored routing logic for better clarity
- Improved state management for navigation
- Better integration with settings page
- Enhanced error boundary handling
- Cleaner separation of layout concerns
- Improved provider context propagation
- Better handling of deep links
- Optimized re-renders with React.memo where appropriate
Code Quality Improvements:
- Reduced prop drilling with better component composition
- Improved TypeScript type safety
- Better separation of concerns
- Enhanced code readability with clearer naming
- Eliminated redundant logic
Performance Optimizations:
- Reduced unnecessary re-renders
- Better memoization of callbacks
- Optimized component tree structure
- Improved event handler efficiency
User Experience:
- Smoother transitions and animations
- Better visual feedback for interactions
- Improved loading states
- More consistent behavior across features
These changes create a more maintainable and performant foundation
for the application's UI layer while improving the overall user
experience with smoother interactions and better visual polish.
* refactor(features): modernize Skills, Prompts and Agents components
Major refactoring of feature components to improve code quality,
user experience, and maintainability.
SkillsPage Component (299 lines refactored):
- Complete rewrite of layout and state management
- Better integration with RepoManagerPanel
- Improved navigation between list and detail views
- Enhanced error handling with user-friendly messages
- Better loading states with skeleton screens
- Optimized re-renders with proper memoization
- Cleaner separation between list and form views
- Improved skill card interactions
- Better responsive design for different screen sizes
RepoManagerPanel Component (370 lines refactored):
- Streamlined repository management workflow
- Enhanced form validation with real-time feedback
- Improved repository list with better visual hierarchy
- Better handling of git operations (clone, pull, delete)
- Enhanced error recovery for network issues
- Cleaner state management reducing complexity
- Improved TypeScript type safety
- Better integration with Skills backend API
- Enhanced loading indicators for async operations
PromptPanel Component (249 lines refactored):
- Modernized layout with FullScreenPanel integration
- Better separation between list and edit modes
- Improved prompt card design with better readability
- Enhanced search and filter functionality
- Cleaner state management for editing workflow
- Better integration with PromptFormPanel
- Improved delete confirmation with safety checks
- Enhanced keyboard navigation support
PromptFormPanel Component (238 lines refactored):
- Streamlined form layout and validation
- Better markdown editor integration
- Real-time preview with syntax highlighting
- Improved validation error display
- Enhanced save/cancel workflow
- Better handling of large prompt content
- Cleaner form state management
- Improved accessibility features
AgentsPanel Component (33 lines modified):
- Minor layout adjustments for consistency
- Better integration with FullScreenPanel
- Improved placeholder states
- Enhanced error boundaries
Type Definitions (types.ts):
- Added 10 new type definitions
- Better type safety for Skills/Prompts/Agents
- Enhanced interfaces for repository management
- Improved typing for form validations
Architecture Improvements:
- Reduced component coupling
- Better prop interfaces with explicit types
- Improved error boundaries
- Enhanced code reusability
- Better testing surface
User Experience Enhancements:
- Smoother transitions between views
- Better visual feedback for actions
- Improved error messages
- Enhanced loading states
- More intuitive navigation flows
- Better responsive layouts
Code Quality:
- Net reduction of 29 lines while adding features
- Improved code organization
- Better naming conventions
- Enhanced documentation
- Cleaner control flow
These changes significantly improve the maintainability and user
experience of core feature components while establishing consistent
patterns for future development.
* style(ui): refine component layouts and improve visual consistency
Comprehensive UI polish across multiple components to enhance visual
design, improve user experience, and maintain consistency.
UsageScriptModal Component (1302 lines refactored):
- Complete layout overhaul for better usability
- Improved script editor with syntax highlighting
- Better template selection interface
- Enhanced test/preview panels with clearer separation
- Improved error feedback and validation messages
- Better modal sizing and responsiveness
- Cleaner tab navigation between sections
- Enhanced code formatting and readability
- Improved loading states for async operations
- Better integration with parent components
MCP Components:
- McpFormModal (42 lines):
* Streamlined form layout
* Better server type selection (stdio/http)
* Improved field grouping and labels
* Enhanced validation feedback
- UnifiedMcpPanel (14 lines):
* Minor layout adjustments
* Better list item spacing
* Improved server status indicators
* Enhanced action button placement
Provider Components:
- ProviderCard (11 lines):
* Refined card layout and spacing
* Better visual hierarchy
* Improved badge placement
* Enhanced hover effects
- ProviderList (5 lines):
* Minor grid layout adjustments
* Better drag-and-drop visual feedback
- GeminiConfigSections (4 lines):
* Field label alignment
* Improved spacing consistency
Editor & Footer Components:
- JsonEditor (13 lines):
* Better editor height management
* Improved error display
* Enhanced syntax highlighting
- UsageFooter (10 lines):
* Refined footer layout
* Better quota display
* Improved refresh button placement
Settings & Environment:
- ImportExportSection (24 lines):
* Better button layout
* Improved action grouping
* Enhanced visual feedback
- EnvWarningBanner (4 lines):
* Refined alert styling
* Better dismiss button placement
Global Styles (index.css):
- Added 11 lines of utility classes
- Improved transition timing
- Better focus indicators
- Enhanced scrollbar styling
- Refined spacing utilities
Design Improvements:
- Consistent spacing using design tokens
- Unified color palette application
- Better typography hierarchy
- Improved shadow system for depth
- Enhanced interactive states (hover, active, focus)
- Better border radius consistency
- Refined animation timings
Accessibility:
- Improved focus indicators
- Better keyboard navigation
- Enhanced screen reader support
- Improved color contrast ratios
Code Quality:
- Net increase of 68 lines due to UsageScriptModal improvements
- Better component organization
- Cleaner style application
- Reduced style duplication
These visual refinements create a more polished and professional
interface while maintaining excellent usability and accessibility
standards across all components.
* chore(i18n): add auto-launch translation keys
Add translation keys for new auto-launch feature to support
multi-language interface.
Translation Keys Added:
- autoLaunch: Label for auto-launch toggle
- autoLaunchDescription: Explanation of auto-launch functionality
- autoLaunchEnabled: Status message when enabled
Languages Updated:
- Chinese (zh.json): 简体中文翻译
- English (en.json): English translations
The translations maintain consistency with existing terminology
and provide clear, user-friendly descriptions of the auto-launch
feature across both supported languages.
* test: update test suites to match component refactoring
Comprehensive test updates to align with recent component refactoring
and new auto-launch functionality.
Component Tests:
- AddProviderDialog.test.tsx (10 lines):
* Updated test cases for new dialog behavior
* Enhanced mock data for preset selection
* Improved assertions for validation
- ImportExportSection.test.tsx (16 lines):
* Updated for new settings page integration
* Enhanced test coverage for error scenarios
* Better mock state management
- McpFormModal.test.tsx (60 lines):
* Extensive updates for form refactoring
* New test cases for multi-app selection
* Enhanced validation testing
* Better coverage of stdio/http server types
- ProviderList.test.tsx (11 lines):
* Updated for new card layout
* Enhanced drag-and-drop testing
- SettingsDialog.test.tsx (96 lines):
* Major updates for SettingsPage migration
* New test cases for auto-launch functionality
* Enhanced integration test coverage
* Better async operation testing
Hook Tests:
- useDirectorySettings.test.tsx (32 lines):
* Updated for refactored hook logic
* Enhanced test coverage for edge cases
- useDragSort.test.tsx (36 lines):
* Simplified test cases
* Better mock implementation
* Improved assertions
- useImportExport tests (16 lines total):
* Updated for new error handling
* Enhanced test coverage
- useMcpValidation.test.tsx (23 lines):
* Updated validation test cases
* Better coverage of error scenarios
- useProviderActions.test.tsx (48 lines):
* Extensive updates for hook refactoring
* New test cases for provider operations
* Enhanced mock data
- useSettings.test.tsx (12 lines):
* New test cases for auto-launch
* Enhanced settings state testing
* Better async operation coverage
Integration Tests:
- App.test.tsx (41 lines):
* Updated for new routing logic
* Enhanced navigation testing
* Better component integration coverage
- SettingsDialog.test.tsx (88 lines):
* Complete rewrite for SettingsPage
* New integration test scenarios
* Enhanced user workflow testing
Mock Infrastructure:
- handlers.ts (117 lines):
* Major updates for MSW handlers
* New handlers for auto-launch commands
* Enhanced error simulation
* Better request/response mocking
- state.ts (37 lines):
* Updated mock state structure
* New state for auto-launch
* Enhanced state reset functionality
- tauriMocks.ts (10 lines):
* Updated mock implementations
* Better type safety
- server.ts & testQueryClient.ts:
* Minor cleanup (2 lines removed)
Test Infrastructure Improvements:
- Better test isolation
- Enhanced mock data consistency
- Improved async operation testing
- Better error scenario coverage
- Enhanced integration test patterns
Coverage Improvements:
- Net increase of 195 lines of test code
- Better coverage of edge cases
- Enhanced error path testing
- Improved integration test scenarios
- Better mock infrastructure
All tests now pass with the refactored components while maintaining
comprehensive coverage of functionality and edge cases.
* style(ui): improve window dragging and provider card styles
* fix(skills): resolve third-party skills installation failure
- Add skills_path field to Skill struct
- Use skills_path to construct correct source path during installation
- Fix installation for repos with custom skill subdirectories
* feat(icon): add icon type system and intelligent inference logic
Introduce a new icon system for provider customization:
- Add IconMetadata and IconPreset interfaces in src/types/icon.ts
- Define structure for icon name, display name, category, keywords
- Support default color configuration per icon
- Implement smart icon inference in src/config/iconInference.ts
- Create iconMappings for 25+ AI providers and cloud platforms
- Include Claude, DeepSeek, Qwen, Kimi, Google, AWS, Azure, etc.
- inferIconForPreset(): match provider name to icon config
- addIconsToPresets(): batch apply icons to preset arrays
- Support fuzzy matching for flexible name recognition
This foundation enables automatic icon assignment when users add
providers, improving visual identification in the provider list.
* feat(ui): add icon picker, color picker and provider icon components
Implement comprehensive icon selection system for provider customization:
## New Components
### ProviderIcon (src/components/ProviderIcon.tsx)
- Render SVG icons by name with automatic fallback
- Display provider initials when icon not found
- Support custom sizing via size prop
- Use dangerouslySetInnerHTML for inline SVG rendering
### IconPicker (src/components/IconPicker.tsx)
- Grid-based icon selection with visual preview
- Real-time search filtering by name and keywords
- Integration with icon metadata for display names
- Responsive grid layout (6-10 columns based on screen)
### ColorPicker (src/components/ColorPicker.tsx)
- 12 preset colors for quick selection
- Native color input for custom color picking
- Hex input field for precise color entry
- Visual feedback for selected color state
## Icon Assets (src/icons/extracted/)
- 38 high-quality SVG icons for AI providers and platforms
- Includes: OpenAI, Claude, DeepSeek, Qwen, Kimi, Gemini, etc.
- Cloud platforms: AWS, Azure, Google Cloud, Cloudflare
- Auto-generated index.ts with getIcon/hasIcon helpers
- Metadata system with searchable keywords per icon
## Build Scripts
- scripts/extract-icons.js: Extract icons from simple-icons
- scripts/generate-icon-index.js: Generate TypeScript index file
* feat(provider): integrate icon system into provider UI components
Add icon customization support to provider management interface:
## Type System Updates
### Provider Interface (src/types.ts)
- Add optional `icon` field for icon name (e.g., "openai", "anthropic")
- Add optional `iconColor` field for hex color (e.g., "#00A67E")
### Form Schema (src/lib/schemas/provider.ts)
- Extend providerSchema with icon and iconColor optional fields
- Maintain backward compatibility with existing providers
## UI Components
### ProviderCard (src/components/providers/ProviderCard.tsx)
- Display ProviderIcon alongside provider name
- Add icon container with hover animation effect
- Adjust layout spacing for icon placement
- Update translate offsets for action buttons
### BasicFormFields (src/components/providers/forms/BasicFormFields.tsx)
- Add icon preview section showing current selection
- Implement fullscreen icon picker dialog
- Auto-apply default color from icon metadata on selection
- Display provider name and icon status in preview
### AddProviderDialog & EditProviderDialog
- Pass icon fields through form submission
- Preserve icon data during provider updates
This enables users to visually distinguish providers in the list
with custom icons, improving UX for multi-provider setups.
* feat(backend): add icon fields to Provider model and default mappings
Extend Rust backend to support provider icon customization:
## Provider Model (src-tauri/src/provider.rs)
- Add `icon: Option<String>` field for icon name
- Add `icon_color: Option<String>` field for hex color
- Use serde rename `iconColor` for frontend compatibility
- Apply skip_serializing_if for clean JSON output
- Update Provider::new() to initialize icon fields as None
## Provider Defaults (src-tauri/src/provider_defaults.rs) [NEW]
- Define ProviderIcon struct with name and color fields
- Create DEFAULT_PROVIDER_ICONS static HashMap with 23 providers:
- AI providers: OpenAI, Anthropic, Claude, Google, Gemini,
DeepSeek, Kimi, Moonshot, Zhipu, MiniMax, Baidu, Alibaba,
Tencent, Meta, Microsoft, Cohere, Perplexity, Mistral, HuggingFace
- Cloud platforms: AWS, Azure, Huawei, Cloudflare
- Implement infer_provider_icon() with exact and fuzzy matching
- Add unit tests for matching logic (exact, fuzzy, case-insensitive)
## Deep Link Support (src-tauri/src/deeplink.rs)
- Initialize icon fields when creating Provider from deep link import
## Module Registration (src-tauri/src/lib.rs)
- Register provider_defaults module
## Dependencies (Cargo.toml)
- Add once_cell for lazy static initialization
This backend support enables icon persistence and future features
like auto-icon inference during provider creation.
* chore(i18n): add translations for icon picker and provider icon
Add Chinese and English translations for icon customization feature:
## Icon Picker (iconPicker)
- search: "Search Icons" / "搜索图标"
- searchPlaceholder: "Enter icon name..." / "输入图标名称..."
- noResults: "No matching icons found" / "未找到匹配的图标"
- category.aiProvider: "AI Providers" / "AI 服务商"
- category.cloud: "Cloud Platforms" / "云平台"
- category.tool: "Dev Tools" / "开发工具"
- category.other: "Other" / "其他"
## Provider Icon (providerIcon)
- label: "Icon" / "图标"
- colorLabel: "Icon Color" / "图标颜色"
- selectIcon: "Select Icon" / "选择图标"
- preview: "Preview" / "预览"
These translations support the new icon picker UI components
and provider form icon selection interface.
* style(ui): refine header layout and AppSwitcher color scheme
Improve application header and component styling:
## App.tsx Header Layout
- Wrap title and settings button in flex container with gap
- Add vertical divider between title and settings icon
- Apply responsive padding (pl-1 sm:pl-2)
- Reformat JSX for better readability (prettier)
- Fix string template formatting in className
## AppSwitcher Color Update
- Change Claude tab gradient from orange/amber to teal/emerald/green
- Update shadow color to match new teal theme
- Change hover color from orange-500 to teal-500
- Align visual style with emerald/teal brand colors
## Dialog Component Cleanup
- Remove default close button (X icon) from DialogContent
- Allow parent components to control close button placement
- Remove unused lucide-react X import
## index.css Header Border
- Add top border (2px solid) to glass-header
- Apply to both light and dark mode variants
- Improve visual separation of header area
These changes enhance visual consistency and modernize the UI
appearance with a cohesive teal color scheme.
* chore(deps): add icon library and update preset configurations
Add dependencies and utility scripts for icon system:
## Dependencies (package.json)
- Add @lobehub/icons-static-svg@1.73.0
- High-quality SVG icon library for AI providers
- Source for extracted icons in src/icons/extracted/
- Update pnpm-lock.yaml accordingly
## Provider Preset Updates (src/config/claudeProviderPresets.ts)
- Add optional `icon` and `iconColor` fields to ProviderPreset interface
- Apply to Anthropic Official preset as example:
- icon: "anthropic"
- iconColor: "#D4915D"
- Future presets can include default icon configurations
## Utility Script (scripts/filter-icons.js) [NEW]
- Helper script for filtering and managing icon assets
- Supports icon discovery and validation workflow
- Complements extract-icons.js and generate-icon-index.js
This completes the icon system infrastructure, providing all
necessary tools and dependencies for icon customization.
* refactor(ui): simplify AppSwitcher styles and migrate to local SVG icons
- Replace complex gradient animations with clean, minimal tab design
- Migrate from @lobehub/icons CDN to local SVG assets for better reliability
- Fix clippy warning in error.rs (use inline format args)
- Improve code formatting in skill service and commands
- Reduce CSS complexity in AppSwitcher component (removed blur effects and gradients)
- Update BrandIcons to use imported local SVG files instead of dynamic image loading
This improves performance, reduces external dependencies, and provides a cleaner UI experience.
* style(ui): hide scrollbars across all browsers and optimize form layout
- Hide scrollbars globally with cross-browser support:
* WebKit browsers (Chrome, Safari, Edge): ::-webkit-scrollbar { display: none }
* Firefox: scrollbar-width: none
* IE 10+: -ms-overflow-style: none
- Remove custom scrollbar styling (width, colors, hover states)
- Reorganize BasicFormFields layout:
* Move icon picker to top center as a clickable preview (80x80)
* Change name and notes fields to horizontal grid layout (md:grid-cols-2)
* Remove icon preview section from bottom
* Improve visual hierarchy and space efficiency
This provides a cleaner, more modern UI with hidden scrollbars while maintaining full scroll functionality.
* refactor(layout): standardize max-width to 60rem and optimize padding structure
- Unify container max-width across components:
* Replace max-w-4xl with max-w-[60rem] in App.tsx provider list
* Replace max-w-5xl with max-w-[60rem] in PromptPanel
* Move padding from header element to inner container for consistent spacing
- Optimize padding hierarchy:
* Remove px-6 from header element, add to inner header container
* Remove px-6 from main element, keep it only in provider list container
* Consolidate PromptPanel padding: move px-6 from nested divs to outer container
* Remove redundant pl-1 sm:pl-2 from logo/title area
- Benefits:
* Consistent 60rem max-width provides better readability on wide screens
* Simplified padding structure reduces CSS complexity
* Cleaner responsive behavior with unified spacing rules
This creates a more maintainable and visually consistent layout system.
* refactor(ui): unify layout system with 60rem max-width and consistent spacing
- Standardize container max-width across all panels:
* Replace max-w-4xl and max-w-5xl with unified max-w-[60rem]
* Apply to SettingsPage, UnifiedMcpPanel, SkillsPage, and FullScreenPanel
* Ensures consistent reading width and visual balance on wide screens
- Optimize padding hierarchy and structure:
* Move px-6 from parent elements to content containers
* FullScreenPanel: Add max-w-[60rem] wrapper to header, content, and footer
* Add border separators (border-t/border-b) to header and footer sections
* Consolidate nested padding in MCP, Skills, and Prompts panels
* Remove redundant padding layers for cleaner CSS
- Simplify component styling:
* MCP list items: Replace card-based layout with modern group-based design
* Remove unnecessary wrapper divs and flatten DOM structure
* Update card hover effects with smooth transitions
* Simplify icon selection dialog (remove description text in BasicFormFields)
- Benefits:
* Consistent 60rem max-width provides optimal readability
* Unified spacing rules reduce maintenance complexity
* Cleaner component hierarchy improves performance
* Better responsive behavior across different screen sizes
* More cohesive visual design language throughout the app
This creates a maintainable, scalable design system foundation.
* feat(deeplink): add Claude model fields support and enhance import dialog
- Add Claude-specific model field support in deeplink import:
* Support model (ANTHROPIC_MODEL) - general default model
* Support haikuModel (ANTHROPIC_DEFAULT_HAIKU_MODEL)
* Support sonnetModel (ANTHROPIC_DEFAULT_SONNET_MODEL)
* Support opusModel (ANTHROPIC_DEFAULT_OPUS_MODEL)
* Backend: Update DeepLinkImportRequest struct to include optional model fields
* Frontend: Add TypeScript type definitions for new model parameters
- Enhance deeplink demo page (deplink.html):
* Add 5 new Claude configuration examples showcasing different model setups
* Add parameter documentation with required/optional tags
* Include basic config (no models), single model, complete 4-model, partial models, and third-party provider examples
* Improve visual design with param-list component and color-coded badges
* Add detailed descriptions for each configuration scenario
- Redesign DeepLinkImportDialog layout:
* Switch from 3-column to compact 2-column grid layout
* Reduce dialog width from 500px to 650px for better content display
* Add dedicated section for Claude model configurations with blue highlight box
* Use uppercase labels and smaller text for more information density
* Add truncation and tooltips for long URLs
* Improve visual hierarchy with spacing and grouping
* Increase z-index to 9999 to ensure dialog appears on top
- Minor UI refinements:
* Update App.tsx layout adjustments
* Optimize McpFormModal styling
* Refine ProviderCard and BasicFormFields components
This enables users to import Claude providers with precise model configurations via deeplink.
* feat(deeplink): add config file support for deeplink import
Support importing provider configuration from embedded or remote config files.
- Add base64 dependency for config content encoding
- Support config, configFormat, and configUrl parameters
- Make homepage/endpoint/apiKey optional when config is provided
- Add config parsing and merging logic
* feat(deeplink): enhance dialog with config file preview
Add config file parsing and preview in deep link import dialog.
- Support Base64 encoded config display
- Add config file source indicator (embedded/remote)
- Parse and display config fields by app type (Claude/Codex/Gemini)
- Mask sensitive values in config preview
- Improve dialog layout and content organization
* refactor(ui): unify dialog styles and improve layout consistency
Standardize dialog and panel components across the application.
- Update dialog background to use semantic color tokens
- Adjust FullScreenPanel max-width to 56rem for better alignment
- Add drag region and prevent body scroll in full-screen panels
- Optimize button sizes and spacing in panel headers
- Apply consistent styling to all dialog-based components
* i18n: add deeplink config preview translations
Add missing translation keys for config file preview feature.
- Add configSource, configEmbedded, configRemote labels
- Add configDetails and configUrl display strings
- Support both Chinese and English versions
* feat(deeplink): enhance test page with v3.8 config file examples
Improve deeplink test page with comprehensive config file import examples.
- Add version badge for v3.8 features
- Add copy-to-clipboard functionality for all deep links
- Add Claude config file import examples (embedded/remote)
- Add Codex config file import examples (auth.json + config.toml)
- Add Gemini config file import examples (.env format)
- Add config generator tool for easy testing
- Update UI with better styling and layout
* feat(settings): add autoSaveSettings for lightweight auto-save
Add optimized auto-save function for General tab settings.
- Add autoSaveSettings method for non-destructive auto-save
- Only trigger system APIs when values actually change
- Avoid unnecessary auto-launch and plugin config updates
- Update tests to cover new functionality
* refactor(settings): simplify settings page layout and auto-save
Reorganize settings page structure and integrate autoSaveSettings.
- Move save button inline within Advanced tab content
- Remove sticky footer for cleaner layout
- Use autoSaveSettings for General tab settings
- Simplify dialog close behavior
- Refactor ImportExportSection layout
* style(providers): optimize card layout and action button sizes
Improve provider card visual density and action buttons.
- Reduce icon button sizes for compact layout
- Adjust drag handle and icon sizes
- Tighten spacing between action buttons
- Update hover translate values for better alignment
* refactor(mcp): improve form modal layout with adaptive height editor
Restructure MCP form modal for better space utilization.
- Split form into upper form fields and lower JSON editor sections
- Add full-height mode support for JsonEditor component
- Use flex layout for editor to fill available space
- Update PromptFormPanel to use full-height editor
- Fix locale text formatting
* style: unify list item styles with semantic colors
Apply consistent styling to list items across components.
- Replace hardcoded colors with semantic tokens in MCP and Prompt list items
- Add glass effect container to EndpointSpeedTest panel
- Format code for better readability
* style: format template literals for better readability
Improve code formatting for conditional className expressions.
- Break long template literals across multiple lines
- Maintain consistent formatting in MCP form and endpoint test components
* feat(deeplink): add config merge command for preview
Expose config merging functionality to frontend for preview.
- Add merge_deeplink_config Tauri command
- Make parse_and_merge_config public for reuse
- Enable frontend to display complete config before import
* feat(deeplink): merge and display config in import dialog
Enhance import dialog to fetch and display complete config.
- Call mergeDeeplinkConfig API when config is present
- Add UTF-8 base64 decoding support for config content
- Add scrollable content area with custom scrollbar styling
- Show complete configuration before user confirms import
* i18n: add config merge error message
Add translation for config file merge error handling.
* style(deeplink): format test page HTML for better readability
Improve HTML formatting in deeplink test page.
- Format multiline attributes for better readability
- Add consistent indentation to nested elements
- Break long lines in buttons and links
* refactor(usage): improve footer layout with two-row design
Reorganize usage footer for better readability and space efficiency.
- Split into two rows: update time + refresh button (row 1), usage stats (row 2)
- Move refresh button to top row next to update time
- Remove card background for cleaner look
- Add fallback text when never updated
- Improve spacing and alignment
- Format template literals for consistency
* feat(database): add SQLite database infrastructure
- Add rusqlite dependency (v0.32.1) and r2d2 connection pooling
- Implement Database module with CRUD operations for providers, MCP servers, prompts, and skills
- Add schema initialization with proper indexes
- Include data migration utilities from JSON config to SQLite
- Support timestamp tracking (created_at, updated_at)
* refactor(core): integrate SQLite database into application core
- Initialize database on app startup with migration from JSON config
- Update AppState to include Database instance alongside MultiAppConfig
- Simplify store module by removing unused session management code
- Add database initialization to app setup flow
- Support both database and legacy config during transition
* refactor(services): migrate service layer to use SQLite database
- Refactor ProviderService to use database queries instead of in-memory config
- Update McpService to fetch and store MCP servers in database
- Migrate PromptService to database-backed storage
- Simplify ConfigService by removing complex transaction logic
- Remove 648 lines of redundant code through database abstraction
* refactor(commands): update command layer to use database API
- Update config commands to query database for providers and settings
- Modify provider commands to pass database handle to services
- Update MCP commands to use database-backed operations
- Refactor prompt and skill commands to leverage database storage
- Simplify import/export commands with database integration
* refactor(backend): update supporting modules for database compatibility
- Add DatabaseError variant to AppError enum
- Update provider module to support database-backed operations
- Modify codex_config to work with new database structure
- Ensure error handling covers database operations
* refactor(frontend): update UI components for database migration
- Update UsageFooter component to handle new data structure
- Modify SkillsPage to work with database-backed skills management
- Ensure frontend compatibility with refactored backend
* feat(skills): add search functionality to Skills page
- Add search input with Search icon in SkillsPage component
- Implement useMemo-based filtering by skill name, description, and directory
- Display search results count when filtering is active
- Show "no results" message when no skills match the search query
- Add i18n translations for search UI (zh/en)
- Maintain responsive layout and consistent styling with existing UI
* refactor(backend): replace unsafe unwrap calls with proper error handling
- Add to_json_string helper for safe JSON serialization
- Add lock_conn macro for safe Mutex locking
- Replace 41 unwrap() calls with proper error handling:
- database.rs: JSON serialization and Mutex operations (31 fixes)
- lib.rs: macOS NSWindow and tray icon handling (3 fixes)
- services/provider.rs: Claude model normalization (1 fix)
- services/prompt.rs: timestamp generation (3 fixes)
- services/skill.rs: directory name extraction (2 fixes)
- mcp.rs: HashMap initialization and type conversions (5 fixes)
- app_config.rs: timestamp fallback (1 fix)
This improves application stability and prevents potential panics.
* feat(init): implement automatic data import on first launch
Add comprehensive first-launch data import system:
Database layer:
- Add is_empty_for_first_import() to detect empty database
- Add init_default_skill_repos() to initialize 3 default skill repositories
Services layer:
- Implement McpService::import_from_claude/codex/gemini()
to import MCP servers from existing config files
- Implement PromptService::import_from_file_on_first_launch()
to import prompt files (CLAUDE.md, AGENTS.md, GEMINI.md)
Startup flow (lib.rs):
- Check if database is empty on startup
- Import existing configurations if detected:
1. Initialize default skill repositories
2. Import provider configurations from live settings
3. Import MCP servers from config files
4. Import prompt files
- All imports are fault-tolerant and logged
This ensures seamless migration from file-based configs to database.
* fix(skills): auto-sync locally installed skills to database
Add automatic synchronization in get_skills command:
- Detect locally installed skills in ~/.claude/skills/
- Auto-add to database if not already tracked
- Ensures existing skills are recognized on first launch
This fixes the issue where user's existing skills were not
imported into the database on initial application run.
* docs(frontend): add code comments and improve formatting
- Add explanatory comment in EditProviderDialog about config assembly
- Improve import formatting in SkillsPage for better readability
* feat(deeplink): display all four Claude model fields in import dialog
- Show haiku/sonnet/opus/multiModel fields conditionally for Claude
- Maintain single model field display for Codex and Gemini
- Add i18n translations for new model field labels (zh/en)
* feat(backend): add database SQL export/import with backup
- Enable rusqlite backup feature for SQL dump support
- Implement export_sql to generate SQLite-compatible SQL dumps
- Implement import_sql with automatic backup before import
- Add snapshot_to_memory to avoid long-held database locks
- Add backup rotation to retain latest 10 backups
- Support atomic import with rollback on failure
* refactor(backend): migrate import/export to use SQL backup
- Reimplement export_config_to_file to use database.export_sql
- Reimplement import_config_from_file to use database.import_sql
- Add sync_current_from_db to sync live configs after import
- Add settings database binding on app initialization
- Remove deprecated JSON-based config import logic
* refactor(backend): migrate settings storage to database
- Add bind_db function to initialize database-backed settings
- Implement load_initial_settings with database fallback
- Replace direct file save with settings store management
- Add SETTINGS_DB static for database binding
- Maintain backward compatibility with file-based settings
- Keep settings.json for legacy migration support
* feat(frontend): update import/export UI for SQL backup
- Change default export filename from .json to .sql
- Update file format to timestamp format (YYYYMMDD_HHMMSS)
- Update error messages to reference SQL files
- Align with backend SQL export/import implementation
* feat(i18n): update translations for SQL backup feature
- Update Chinese translations for SQL import/export
- Update English translations for SQL import/export
- Change terminology from 'config file' to 'SQL backup'
- Update error messages and UI hints
* fix(backend): remove unnecessary dereference in backup operation
- Simplify Backup::new call by removing redundant dereference
- MutexGuard already implements DerefMut
* feat(icons): add PackyCode provider icon support
Add PackyCode as a supported AI provider icon with proper metadata
and filtering configuration.
Changes:
- Add 'packycode' to icon filter whitelist in filter-icons.js
- Register PackyCode metadata with display name, category, and keywords
- Import PackyCode SVG icon file
- Export icon through index.ts for use in provider configurations
The PackyCode icon uses currentColor to adapt to theme styling.
* feat(utils): add base64 encoding utility functions
Add reusable base64 encoding/decoding utility functions for handling
binary data and string conversions in deeplink imports.
Features:
- encodeBase64: Encode string to base64
- decodeBase64: Decode base64 to string
- Uses browser-native btoa/atob with proper UTF-8 handling
This utility will be used for encoding prompt content and configuration
files in deeplink URLs.
* feat(backend): add in-memory database mode for testing
Add support for creating in-memory SQLite database instances to
improve test isolation and performance.
Changes:
- Add Database::memory() constructor for in-memory database
- Enable foreign key constraints for data integrity
- Export Database type from lib.rs for test usage
- Initialize tables automatically on memory database creation
This enables unit tests to run without filesystem dependencies and
provides faster test execution with proper cleanup.
* refactor(deeplink): extend support for multi-resource imports
Extend the deeplink import system to support importing multiple
resource types beyond providers: prompts, MCP servers, and skills.
Breaking changes:
- DeepLinkImportRequest: Convert required fields to Optional to
support different resource types (app, name, homepage, endpoint,
apiKey are now Option<String>)
- Add resource-specific fields for prompt, mcp, and skill imports
New features:
- parse_prompt_deeplink: Parse prompt import URLs with base64 content
- parse_mcp_deeplink: Parse MCP server import URLs with config
- parse_skill_deeplink: Parse GitHub skill repository URLs
- import_prompt_from_deeplink: Import prompts to database
- import_mcp_from_deeplink: Batch import MCP servers with multi-app support
- import_skill_from_deeplink: Clone and install skill repositories
Data model additions:
- Prompt fields: content (base64), description, enabled
- MCP fields: apps (comma-separated), config, config_format
- Skill fields: repo, directory, branch, skills_path
- Common fields: icon (provider icon name)
McpImportResult type:
- imported_count: Number of successfully imported servers
- imported_ids: List of imported server IDs
- failed: List of failed imports with error messages
URL format examples:
- Prompt: ccswitch://v1/import?resource=prompt&app=claude&name=...&content=...
- MCP: ccswitch://v1/import?resource=mcp&apps=claude,codex&config=...
- Skill: ccswitch://v1/import?resource=skill&repo=owner/name&directory=...
This refactor enables one-click sharing of prompts, MCP configurations,
and skill repositories via deeplink URLs.
* feat(backend): add unified deeplink import command
Add a new unified command handler for importing all resource types
via deeplinks, replacing the legacy provider-only import command.
Changes:
- Add import_from_deeplink_unified command supporting all resource types
- Keep import_from_deeplink for backward compatibility (now marked legacy)
- Route imports based on request.resource field (provider/prompt/mcp/skill)
- Return typed ImportResult with resource-specific data
Return types:
- Provider: { type: "provider", id: string }
- Prompt: { type: "prompt", id: string }
- MCP: { type: "mcp", importedCount, importedIds, failed }
- Skill: { type: "skill", key: string }
The unified handler simplifies frontend logic by providing consistent
return types and error handling across all resource types.
* feat(frontend): extend deeplink API for multi-resource support
Update the frontend deeplink API to support importing multiple
resource types with proper TypeScript typing.
Changes:
- Add ResourceType union type: "provider" | "prompt" | "mcp" | "skill"
- Convert DeepLinkImportRequest fields to optional (matching backend)
- Add resource-specific field types (prompt, mcp, skill)
- Add ImportResult discriminated union for type-safe results
- Add McpImportResult interface for batch import results
- Update importFromDeeplink to use unified command
Type safety improvements:
- ImportResult discriminated union ensures proper type narrowing
- Each result type has its own specific return data structure
- Frontend can pattern match on result.type for correct handling
Breaking change:
- importFromDeeplink now returns ImportResult instead of string
- Callers must handle all resource types appropriately
* feat(frontend): add resource-specific confirmation dialog components
Add specialized confirmation UI components for each deeplink import
resource type (Prompt, MCP, Skill).
Components:
- PromptConfirmation: Display prompt name, app, description, and
content preview with markdown rendering
- McpConfirmation: Show MCP server list with target apps, supports
batch import display
- SkillConfirmation: Display GitHub repository info with branch and
directory details
Features:
- Consistent card-based layout with proper spacing
- Sensitive data masking (API keys shown as dots)
- Icon support for providers (ProviderIcon component)
- Badge components for visual status indicators
- Responsive design with proper text overflow handling
Each component focuses on displaying the most relevant information
for users to make informed import decisions.
* feat(frontend): update DeepLinkImportDialog for multi-resource imports
Refactor the main deeplink import dialog to handle all resource types
with proper confirmation UI and post-import actions.
Key changes:
- Add resource-specific confirmation components (Prompt/MCP/Skill)
- Implement typed result handling with discriminated unions
- Add MCP result type guard for backward compatibility
- Implement resource-specific cache invalidation strategies
- Add custom event dispatching for non-React-Query resources
Import flow improvements:
- Provider: Invalidate provider queries, show success toast
- Prompt: Dispatch "prompt-imported" event, trigger manual refresh
- MCP: Aggressive cache invalidation with refetchQueries, handle
partial success (show warning if some imports failed)
- Skill: Force refetch skills query with refetchType: "all"
Error handling:
- Graceful fallback for legacy backend responses (no type field)
- MCP-specific result detection via type guard
- Detailed error messages in toasts
UI improvements:
- Dynamic dialog title based on resource type
- Resource-specific confirmation content rendering
- Better visual feedback during import process
* feat(frontend): add deeplink import event listeners and UI improvements
Add event-driven refresh logic for deeplink imports and enhance
Skills page filtering capabilities.
PromptPanel changes:
- Add "prompt-imported" custom event listener
- Auto-reload prompts when deeplink import completes
- Filter events by app ID to avoid unnecessary refreshes
- Clean up event listener on component unmount
SkillsPage improvements:
- Add installation status filter (all/installed/uninstalled)
- Implement Select component for filter dropdown
- Combine status filter with existing search functionality
- Update filtered skills memo to include both filters
- Improve responsive layout for search and filter controls
Event flow:
1. DeepLinkImportDialog dispatches "prompt-imported" event
2. PromptPanel listens for event matching its app
3. Panel triggers reload to show newly imported prompt
4. Similar pattern can be used for other non-React-Query resources
These improvements enable seamless UI updates after deeplink imports
without requiring manual page refresh.
* test(deeplink): migrate tests to use in-memory database
Update deeplink import tests to use the new in-memory database
instead of filesystem-based configuration.
Changes:
- Replace MultiAppConfig with Database-based AppState
- Use Database::memory() for isolated test instances
- Update provider verification to query database directly
- Add icon field verification in test assertions
- Remove filesystem config.json validation (now DB-backed)
Test improvements:
- Faster execution (no disk I/O)
- Better isolation (each test gets fresh DB instance)
- No cleanup required (memory DB auto-discarded)
- Consistent with v3.8+ storage architecture
Updated tests:
- deeplink_import_claude_provider_persists_to_db
- deeplink_import_codex_provider_builds_auth_and_config
Both tests verify that deeplink imports correctly persist provider
data to the database with all expected fields.
* feat(i18n): add translations for deeplink and skills features
Add internationalization support for new deeplink import features
and skills page filtering.
Deeplink translations:
- Add "icon" field label for provider icon selection
- Both Chinese ("图标") and English ("Icon") translations
Skills page translations:
- Add filter placeholder and options
- Filter states: "all", "installed", "uninstalled"
- Chinese: "全部", "已安装", "未安装"
- English: "All", "Installed", "Not installed"
These translations ensure consistent multilingual support for the
new multi-resource deeplink import system and improved skills
management UI.
* chore: add deeplink testing HTML page
Add a local HTML page for testing deeplink protocol functionality
during development.
This page allows developers to:
- Test different deeplink URL formats (provider/prompt/mcp/skill)
- Verify URL parsing and parameter encoding
- Quickly validate deeplink imports without external tools
- Debug protocol registration on different platforms
Not intended for production use, only for development testing.
* refactor(ui): improve icon rendering consistency and spacing
Standardize icon sizes and improve rendering consistency across the
application interface.
Changes:
- ProviderIcon: Add fontSize sync with size prop for embedded SVG
scaling, improve fallback text sizing calculation
- AppSwitcher: Replace brand icon components with unified ProviderIcon,
standardize icon size to 20px across all app tabs
- ProviderCard: Reduce icon size from 26px to 20px for better visual
balance in card layout
- DeepLinkImportDialog: Increase confirmation icon size from 64px to
80px for better visibility
- BasicFormFields: Reduce icon picker button spacing from space-y-6 to
space-y-2 for improved layout density
Icon rendering improvements:
- Embedded SVGs now properly scale with fontSize set to match size prop
- Fallback initials use responsive font sizing (50% of icon size,
minimum 12px)
- Consistent 20px standard for navigation and card icons
- Better visual hierarchy with appropriate sizing for different contexts
This refactor improves visual consistency and makes icon rendering
more predictable across different components.
2715 lines
145 KiB
HTML
2715 lines
145 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>CC Switch 深链接测试</title>
|
||
<style>
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
padding: 20px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
background: white;
|
||
border-radius: 16px;
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||
overflow: hidden;
|
||
}
|
||
|
||
.header {
|
||
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
||
color: white;
|
||
padding: 40px;
|
||
text-align: center;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 32px;
|
||
margin-bottom: 10px;
|
||
}
|
||
|
||
.header p {
|
||
font-size: 16px;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.content {
|
||
padding: 40px;
|
||
}
|
||
|
||
.section {
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.section h2 {
|
||
color: #2c3e50;
|
||
font-size: 24px;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 10px;
|
||
border-bottom: 2px solid #ecf0f1;
|
||
}
|
||
|
||
.version-badge {
|
||
display: inline-block;
|
||
background: linear-gradient(135deg, #f39c12 0%, #e67e22 100%);
|
||
color: white;
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
margin-left: 8px;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.link-card {
|
||
background: #f8f9fa;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
margin-bottom: 20px;
|
||
border: 2px solid #e9ecef;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.link-card:hover {
|
||
border-color: #3498db;
|
||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.15);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.link-card h3 {
|
||
color: #2c3e50;
|
||
font-size: 20px;
|
||
margin-bottom: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.link-card .description {
|
||
color: #7f8c8d;
|
||
font-size: 14px;
|
||
margin-bottom: 16px;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.deep-link {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
|
||
color: white;
|
||
padding: 12px 24px;
|
||
text-decoration: none;
|
||
border-radius: 8px;
|
||
font-weight: 500;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 8px rgba(52, 152, 219, 0.3);
|
||
}
|
||
|
||
.deep-link:hover {
|
||
background: linear-gradient(135deg, #2980b9 0%, #1f6391 100%);
|
||
box-shadow: 0 4px 12px rgba(52, 152, 219, 0.4);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.deep-link:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.info-box {
|
||
background: #fff3cd;
|
||
border-left: 4px solid #ffc107;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.info-box h4 {
|
||
color: #856404;
|
||
margin-bottom: 8px;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.info-box ul {
|
||
list-style: disc;
|
||
margin-left: 20px;
|
||
color: #856404;
|
||
font-size: 14px;
|
||
line-height: 1.8;
|
||
padding-left: 20px;
|
||
}
|
||
|
||
.generator-section {
|
||
background: #e8f4f8;
|
||
border-radius: 12px;
|
||
padding: 30px;
|
||
margin-top: 40px;
|
||
}
|
||
|
||
.generator-section h2 {
|
||
color: #2c3e50;
|
||
margin-bottom: 24px;
|
||
border-bottom: 2px solid #3498db;
|
||
padding-bottom: 10px;
|
||
}
|
||
|
||
.form-group {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.form-group label {
|
||
display: block;
|
||
margin-bottom: 8px;
|
||
color: #2c3e50;
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.form-group input,
|
||
.form-group select,
|
||
.form-group textarea {
|
||
width: 100%;
|
||
padding: 12px;
|
||
border: 2px solid #dee2e6;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
transition: border-color 0.3s ease;
|
||
}
|
||
|
||
.form-group input:focus,
|
||
.form-group select:focus,
|
||
.form-group textarea:focus {
|
||
outline: none;
|
||
border-color: #3498db;
|
||
}
|
||
|
||
.btn {
|
||
background: linear-gradient(135deg, #27ae60 0%, #229954 100%);
|
||
color: white;
|
||
padding: 14px 28px;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 8px rgba(39, 174, 96, 0.3);
|
||
}
|
||
|
||
.btn:hover {
|
||
background: linear-gradient(135deg, #229954 0%, #1e8449 100%);
|
||
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.4);
|
||
transform: translateY(-1px);
|
||
}
|
||
|
||
.btn:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
.result-box {
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin-top: 20px;
|
||
border: 2px solid #3498db;
|
||
}
|
||
|
||
.result-box strong {
|
||
color: #2c3e50;
|
||
font-size: 16px;
|
||
}
|
||
|
||
.result-text {
|
||
background: #f8f9fa;
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
margin: 12px 0;
|
||
word-break: break-all;
|
||
font-family: monospace;
|
||
font-size: 13px;
|
||
color: #2c3e50;
|
||
border: 1px solid #dee2e6;
|
||
}
|
||
|
||
.btn-copy {
|
||
background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);
|
||
margin-right: 10px;
|
||
}
|
||
|
||
.btn-copy:hover {
|
||
background: linear-gradient(135deg, #8e44ad 0%, #7d3c98 100%);
|
||
}
|
||
|
||
.app-badge {
|
||
display: inline-block;
|
||
padding: 4px 12px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.badge-claude {
|
||
background: #e8f4f8;
|
||
color: #3498db;
|
||
}
|
||
|
||
.badge-codex {
|
||
background: #fef5e7;
|
||
color: #f39c12;
|
||
}
|
||
|
||
.badge-gemini {
|
||
background: #fdeef4;
|
||
color: #e91e63;
|
||
}
|
||
|
||
.param-list {
|
||
background: #f8f9fa;
|
||
border-left: 3px solid #3498db;
|
||
padding: 12px;
|
||
border-radius: 6px;
|
||
margin: 12px 0;
|
||
font-size: 13px;
|
||
line-height: 1.8;
|
||
color: #495057;
|
||
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
|
||
}
|
||
|
||
.param-tag {
|
||
display: inline-block;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
margin-right: 6px;
|
||
background: #3498db;
|
||
color: white;
|
||
}
|
||
|
||
.param-tag.optional {
|
||
background: #95a5a6;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.header h1 {
|
||
font-size: 24px;
|
||
}
|
||
|
||
.content {
|
||
padding: 20px;
|
||
}
|
||
|
||
.generator-section {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="container">
|
||
<div class="header">
|
||
<h1>🔗 CC Switch 深链接测试</h1>
|
||
<p>点击下方链接测试深链接导入功能</p>
|
||
</div>
|
||
|
||
<div class="content">
|
||
<!-- Claude 示例 -->
|
||
<div class="section">
|
||
<h2>Claude Code 供应商</h2>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
基础配置(无模型)
|
||
</h3>
|
||
<p class="description">
|
||
最简单的 Claude 配置,仅包含必需参数(API Key、端点等),不指定任何模型,使用 Claude Code 默认值。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=claude, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Basic&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-basic-key¬es=%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE%EF%BC%8C%E6%97%A0%E6%A8%A1%E5%9E%8B%E6%8C%87%E5%AE%9A"
|
||
class="deep-link">
|
||
📥 导入基础配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Basic&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-basic-key¬es=%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE%EF%BC%8C%E6%97%A0%E6%A8%A1%E5%9E%8B%E6%8C%87%E5%AE%9A', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
带默认模型
|
||
</h3>
|
||
<p class="description">
|
||
设置通用默认模型(ANTHROPIC_MODEL),适用于只需要一个固定模型的场景。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=claude, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=claude-haiku-4.1</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Claude%20with%20Model&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-model-key&model=claude-haiku-4.1¬es=%E5%B8%A6%E9%BB%98%E8%AE%A4%E6%A8%A1%E5%9E%8B"
|
||
class="deep-link">
|
||
📥 导入带默认模型
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Claude%20with%20Model&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-model-key&model=claude-haiku-4.1¬es=%E5%B8%A6%E9%BB%98%E8%AE%A4%E6%A8%A1%E5%9E%8B', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
完整配置(四种模型)
|
||
</h3>
|
||
<p class="description">
|
||
设置所有四种模型字段:默认模型、Haiku 模型、Sonnet 模型、Opus 模型。适用于需要精细控制不同模型系列的场景。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=claude, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=claude-sonnet-4.5</strong>,
|
||
<strong>haikuModel=claude-haiku-4.1</strong>, <strong>sonnetModel=claude-sonnet-4.5</strong>,
|
||
<strong>opusModel=claude-opus-4</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Complete&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-complete-key&model=claude-sonnet-4.5&haikuModel=claude-haiku-4.1&sonnetModel=claude-sonnet-4.5&opusModel=claude-opus-4¬es=%E5%AE%8C%E6%95%B4%E6%A8%A1%E5%9E%8B%E9%85%8D%E7%BD%AE"
|
||
class="deep-link">
|
||
📥 导入完整配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Complete&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-complete-key&model=claude-sonnet-4.5&haikuModel=claude-haiku-4.1&sonnetModel=claude-sonnet-4.5&opusModel=claude-opus-4¬es=%E5%AE%8C%E6%95%B4%E6%A8%A1%E5%9E%8B%E9%85%8D%E7%BD%AE', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
部分模型配置
|
||
</h3>
|
||
<p class="description">
|
||
仅设置特定的模型字段(例如只设置 Haiku 和 Sonnet),其他模型使用默认值。演示参数灵活性。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=claude, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>haikuModel=claude-haiku-4.1</strong>,
|
||
<strong>sonnetModel=claude-sonnet-4.5</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Partial&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-partial-key&haikuModel=claude-haiku-4.1&sonnetModel=claude-sonnet-4.5¬es=%E9%83%A8%E5%88%86%E6%A8%A1%E5%9E%8B%E9%85%8D%E7%BD%AE"
|
||
class="deep-link">
|
||
📥 导入部分模型配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Partial&homepage=https%3A%2F%2Fclaude.ai&endpoint=https%3A%2F%2Fapi.anthropic.com%2Fv1&apiKey=sk-ant-test-partial-key&haikuModel=claude-haiku-4.1&sonnetModel=claude-sonnet-4.5¬es=%E9%83%A8%E5%88%86%E6%A8%A1%E5%9E%8B%E9%85%8D%E7%BD%AE', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
第三方供应商示例
|
||
</h3>
|
||
<p class="description">
|
||
使用第三方供应商的 API 端点和自定义模型名称,演示兼容性。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=claude, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=custom-claude-v1</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Third-Party%20Claude&homepage=https%3A%2F%2Fthirdparty.com&endpoint=https%3A%2F%2Fapi.thirdparty.com%2Fv1&apiKey=sk-third-party-key&model=custom-claude-v1¬es=%E7%AC%AC%E4%B8%89%E6%96%B9%E4%BE%9B%E5%BA%94%E5%95%86%E7%A4%BA%E4%BE%8B"
|
||
class="deep-link">
|
||
📥 导入第三方供应商
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Third-Party%20Claude&homepage=https%3A%2F%2Fthirdparty.com&endpoint=https%3A%2F%2Fapi.thirdparty.com%2Fv1&apiKey=sk-third-party-key&model=custom-claude-v1¬es=%E7%AC%AC%E4%B8%89%E6%96%B9%E4%BE%9B%E5%BA%94%E5%95%86%E7%A4%BA%E4%BE%8B', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Codex 示例 -->
|
||
<div class="section">
|
||
<h2>Codex 供应商</h2>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-codex">Codex</span>
|
||
基础配置
|
||
</h3>
|
||
<p class="description">
|
||
OpenAI 官方 API 基础配置,仅包含必需参数。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=codex, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Basic&homepage=https%3A%2F%2Fopenai.com&endpoint=https%3A%2F%2Fapi.openai.com%2Fv1&apiKey=sk-test-basic-openai-key¬es=%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE"
|
||
class="deep-link">
|
||
📥 导入基础配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Basic&homepage=https%3A%2F%2Fopenai.com&endpoint=https%3A%2F%2Fapi.openai.com%2Fv1&apiKey=sk-test-basic-openai-key¬es=%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-codex">Codex</span>
|
||
带默认模型
|
||
</h3>
|
||
<p class="description">
|
||
OpenAI API 配置,指定默认模型为 gpt-5.1。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=codex, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=gpt-5.1</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Official&homepage=https%3A%2F%2Fopenai.com&endpoint=https%3A%2F%2Fapi.openai.com%2Fv1&apiKey=sk-test-demo-openai-key-67890&model=gpt-5.1¬es=OpenAI%20%E5%AE%98%E6%96%B9%E6%9C%8D%E5%8A%A1"
|
||
class="deep-link">
|
||
📥 导入 OpenAI Official
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Official&homepage=https%3A%2F%2Fopenai.com&endpoint=https%3A%2F%2Fapi.openai.com%2Fv1&apiKey=sk-test-demo-openai-key-67890&model=gpt-5.1¬es=OpenAI%20%E5%AE%98%E6%96%B9%E6%9C%8D%E5%8A%A1', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-codex">Codex</span>
|
||
Azure OpenAI
|
||
</h3>
|
||
<p class="description">
|
||
Azure 部署的 OpenAI 服务示例。适合企业用户使用 Azure 云服务。默认模型 gpt-5.1。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=codex, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=gpt-5.1</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=codex&name=Azure%20OpenAI&homepage=https%3A%2F%2Fazure.microsoft.com%2Fopenai&endpoint=https%3A%2F%2Fyour-resource.openai.azure.com%2F&apiKey=azure-test-api-key-xyz&model=gpt-5.1¬es=Azure%20%E4%BC%81%E4%B8%9A%E7%89%88%E6%9C%AC"
|
||
class="deep-link">
|
||
📥 导入 Azure OpenAI
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=codex&name=Azure%20OpenAI&homepage=https%3A%2F%2Fazure.microsoft.com%2Fopenai&endpoint=https%3A%2F%2Fyour-resource.openai.azure.com%2F&apiKey=azure-test-api-key-xyz&model=gpt-5.1¬es=Azure%20%E4%BC%81%E4%B8%9A%E7%89%88%E6%9C%AC', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-codex">Codex</span>
|
||
第三方兼容 API
|
||
</h3>
|
||
<p class="description">
|
||
使用兼容 OpenAI API 格式的第三方服务(如 Together AI、Groq 等)。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=codex, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=mixtral-8x7b</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=codex&name=Third-Party%20API&homepage=https%3A%2F%2Fthirdparty.com&endpoint=https%3A%2F%2Fapi.thirdparty.com%2Fv1&apiKey=sk-third-party-key&model=mixtral-8x7b¬es=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%85%BC%E5%AE%B9%E6%9C%8D%E5%8A%A1"
|
||
class="deep-link">
|
||
📥 导入第三方 API
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=codex&name=Third-Party%20API&homepage=https%3A%2F%2Fthirdparty.com&endpoint=https%3A%2F%2Fapi.thirdparty.com%2Fv1&apiKey=sk-third-party-key&model=mixtral-8x7b¬es=%E7%AC%AC%E4%B8%89%E6%96%B9%E5%85%BC%E5%AE%B9%E6%9C%8D%E5%8A%A1', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Gemini 示例 -->
|
||
<div class="section">
|
||
<h2>Gemini 供应商</h2>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-gemini">Gemini</span>
|
||
基础配置
|
||
</h3>
|
||
<p class="description">
|
||
Google Gemini 官方 API 基础配置,仅包含必需参数。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=gemini, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini%20Basic&homepage=https%3A%2F%2Fai.google.dev&endpoint=https%3A%2F%2Fgenerativelanguage.googleapis.com%2Fv1beta&apiKey=AIzaSy-test-basic-key¬es=%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE"
|
||
class="deep-link">
|
||
📥 导入基础配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini%20Basic&homepage=https%3A%2F%2Fai.google.dev&endpoint=https%3A%2F%2Fgenerativelanguage.googleapis.com%2Fv1beta&apiKey=AIzaSy-test-basic-key¬es=%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-gemini">Gemini</span>
|
||
Google Gemini Official
|
||
</h3>
|
||
<p class="description">
|
||
导入 Google Gemini 官方 API 配置。默认模型 gemini-3-pro-preview。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=gemini, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=gemini-3-pro-preview</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini&homepage=https%3A%2F%2Fai.google.dev&endpoint=https%3A%2F%2Fgenerativelanguage.googleapis.com%2Fv1beta&apiKey=AIzaSy-test-demo-key-abc123&model=gemini-3-pro-preview¬es=Google%20AI%20%E5%AE%98%E6%96%B9%E6%9C%8D%E5%8A%A1"
|
||
class="deep-link">
|
||
📥 导入 Google Gemini
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini&homepage=https%3A%2F%2Fai.google.dev&endpoint=https%3A%2F%2Fgenerativelanguage.googleapis.com%2Fv1beta&apiKey=AIzaSy-test-demo-key-abc123&model=gemini-3-pro-preview¬es=Google%20AI%20%E5%AE%98%E6%96%B9%E6%9C%8D%E5%8A%A1', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-gemini">Gemini</span>
|
||
Gemini 测试环境
|
||
</h3>
|
||
<p class="description">
|
||
公司内部 Gemini 测试环境配置示例。用于验证 Gemini 相关深链接导入流程。默认模型 gemini-3-pro-preview。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=gemini, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=gemini-3-pro-preview</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=gemini&name=%E5%85%AC%E5%8F%B8%20Gemini%20%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83&homepage=https%3A%2F%2Fgemini-test.company.com&endpoint=https%3A%2F%2Fapi-gemini-test.company.com%2Fv1beta&apiKey=sk-gemini-test-company-key&model=gemini-3-pro-preview¬es=%E5%85%AC%E5%8F%B8%E5%86%85%E9%83%A8%20Gemini%20%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83%EF%BC%8C%E4%BB%85%E4%BE%9B%E5%BC%80%E5%8F%91%E4%BD%BF%E7%94%A8"
|
||
class="deep-link">
|
||
📥 导入 Gemini 测试环境
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=gemini&name=%E5%85%AC%E5%8F%B8%20Gemini%20%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83&homepage=https%3A%2F%2Fgemini-test.company.com&endpoint=https%3A%2F%2Fapi-gemini-test.company.com%2Fv1beta&apiKey=sk-gemini-test-company-key&model=gemini-3-pro-preview¬es=%E5%85%AC%E5%8F%B8%E5%86%85%E9%83%A8%20Gemini%20%E6%B5%8B%E8%AF%95%E7%8E%AF%E5%A2%83%EF%BC%8C%E4%BB%85%E4%BE%9B%E5%BC%80%E5%8F%91%E4%BD%BF%E7%94%A8', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-gemini">Gemini</span>
|
||
第三方兼容服务
|
||
</h3>
|
||
<p class="description">
|
||
使用第三方提供的 Gemini API 兼容服务,支持自定义模型名称。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=gemini, name, homepage, endpoint,
|
||
apiKey<br>
|
||
<span class="param-tag optional">可选</span> <strong>model=gemini-custom-v2</strong>, notes
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=gemini&name=Third-Party%20Gemini&homepage=https%3A%2F%2Fthirdparty-gemini.com&endpoint=https%3A%2F%2Fapi.thirdparty-gemini.com%2Fv1&apiKey=tpg-test-key-xyz&model=gemini-custom-v2¬es=%E7%AC%AC%E4%B8%89%E6%96%B9%20Gemini%20%E5%85%BC%E5%AE%B9%E6%9C%8D%E5%8A%A1"
|
||
class="deep-link">
|
||
📥 导入第三方服务
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=gemini&name=Third-Party%20Gemini&homepage=https%3A%2F%2Fthirdparty-gemini.com&endpoint=https%3A%2F%2Fapi.thirdparty-gemini.com%2Fv1&apiKey=tpg-test-key-xyz&model=gemini-custom-v2¬es=%E7%AC%AC%E4%B8%89%E6%96%B9%20Gemini%20%E5%85%BC%E5%AE%B9%E6%9C%8D%E5%8A%A1', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 配置文件导入示例 (v3.8+) -->
|
||
<div class="section">
|
||
<h2>📦 配置文件导入示例 <span class="version-badge">v3.8+</span></h2>
|
||
|
||
<!-- Claude 配置文件导入 -->
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
完整 JSON 配置导入
|
||
</h3>
|
||
<p class="description">
|
||
通过 Base64 编码的 JSON 配置文件导入完整配置,包含所有四种模型和端点信息。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app=claude, name<br>
|
||
<span class="param-tag optional">可选</span> <strong>config</strong> (Base64 JSON),
|
||
<strong>configFormat=json</strong>
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Complete&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0FVVEhfVE9LRU4iOiJzay1hbnQtdGVzdC1rZXkxMjMiLCJBTlRIUk9QSUNfQkFTRV9VUkwiOiJodHRwczovL2FwaS5hbnRocm9waWMuY29tL3YxIiwiQU5USFJPUElDX01PREVMIjoiY2xhdWRlLXNvbm5ldC00LjUiLCJBTlRIUk9QSUNfREVGQVVMVF9IQUlLVV9NT0RFTCI6ImNsYXVkZS1oYWlrdS00LjEiLCJBTlRIUk9QSUNfREVGQVVMVF9TT05ORVRfTU9ERUwiOiJjbGF1ZGUtc29ubmV0LTQuNSIsIkFOVEhST1BJQ19ERUZBVUxUX09QVVNfTU9ERUwiOiJjbGF1ZGUtb3B1cy00In19"
|
||
class="deep-link">
|
||
📥 导入完整配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Claude%20Complete&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0FVVEhfVE9LRU4iOiJzay1hbnQtdGVzdC1rZXkxMjMiLCJBTlRIUk9QSUNfQkFTRV9VUkwiOiJodHRwczovL2FwaS5hbnRocm9waWMuY29tL3YxIiwiQU5USFJPUElDX01PREVMIjoiY2xhdWRlLXNvbm5ldC00LjUiLCJBTlRIUk9QSUNfREVGQVVMVF9IQUlLVV9NT0RFTCI6ImNsYXVkZS1oYWlrdS00LjEiLCJBTlRIUk9QSUNfREVGQVVMVF9TT05ORVRfTU9ERUwiOiJjbGF1ZGUtc29ubmV0LTQuNSIsIkFOVEhST1BJQ19ERUZBVUxUX09QVVNfTU9ERUwiOiJjbGF1ZGUtb3B1cy00In19', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
|
||
{<br>
|
||
"env": {<br>
|
||
"ANTHROPIC_AUTH_TOKEN": "sk-ant-test-key123",<br>
|
||
"ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1",<br>
|
||
"ANTHROPIC_MODEL": "claude-sonnet-4.5",<br>
|
||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4.1",<br>
|
||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4.5",<br>
|
||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4"<br>
|
||
}<br>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- URL 参数覆盖配置文件 -->
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
配置 + URL 参数覆盖
|
||
</h3>
|
||
<p class="description">
|
||
配置文件提供基础设置,URL 参数覆盖 API Key。URL 参数优先级最高。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> name, config<br>
|
||
<span class="param-tag optional">覆盖</span> <strong>apiKey</strong> (覆盖配置文件中的值)
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=My%20Custom&apiKey=sk-ant-my-new-key&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0JBU0VfVVJMIjoiaHR0cHM6Ly9hcGkuYW50aHJvcGljLmNvbS92MSIsIkFOVEhST1BJQ19NT0RFTCI6ImNsYXVkZS1zb25uZXQtNC41In19"
|
||
class="deep-link">
|
||
📥 导入混合配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=My%20Custom&apiKey=sk-ant-my-new-key&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0JBU0VfVVJMIjoiaHR0cHM6Ly9hcGkuYW50aHJvcGljLmNvbS92MSIsIkFOVEhST1BJQ19NT0RFTCI6ImNsYXVkZS1zb25uZXQtNC41In19', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
|
||
{<br>
|
||
"env": {<br>
|
||
"ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1",<br>
|
||
"ANTHROPIC_MODEL": "claude-sonnet-4.5"<br>
|
||
}<br>
|
||
}<br>
|
||
<div style="color: #f39c12; margin-top: 8px;">// URL 参数覆盖: apiKey=sk-ant-my-new-key</div>
|
||
</div>
|
||
<div
|
||
style="margin-top: 12px; padding: 10px; background: #fff3cd; border-left: 4px solid #ffc107; border-radius: 4px; font-size: 13px;">
|
||
<strong>优先级规则:</strong> URL 参数 (apiKey) > 配置文件 (endpoint, model)
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Codex TOML 配置导入 -->
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-codex">Codex</span>
|
||
TOML 格式配置导入
|
||
</h3>
|
||
<p class="description">
|
||
Codex 使用 TOML 格式的配置文件,包含 auth 和 config 两部分。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> name, config<br>
|
||
<span class="param-tag optional">可选</span> <strong>configFormat=json</strong> (Codex 配置为 JSON
|
||
包装的 TOML)
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Complete&configFormat=json&config=eyJhdXRoIjp7Ik9QRU5BSV9BUElfS0VZIjoic2stcHJvai10ZXN0LWtleTEyMyJ9LCJjb25maWciOiJbbW9kZWxfcHJvdmlkZXJzLm9wZW5haV1cbmJhc2VfdXJsID0gXCJodHRwczovL2FwaS5vcGVuYWkuY29tL3YxXCJcblxuW2dlbmVyYWxdXG5tb2RlbCA9IFwiZ3B0LTUuMVwiIn0="
|
||
class="deep-link">
|
||
📥 导入 Codex 配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=codex&name=OpenAI%20Complete&configFormat=json&config=eyJhdXRoIjp7Ik9QRU5BSV9BUElfS0VZIjoic2stcHJvai10ZXN0LWtleTEyMyJ9LCJjb25maWciOiJbbW9kZWxfcHJvdmlkZXJzLm9wZW5haV1cbmJhc2VfdXJsID0gXCJodHRwczovL2FwaS5vcGVuYWkuY29tL3YxXCJcblxuW2dlbmVyYWxdXG5tb2RlbCA9IFwiZ3B0LTUuMVwiIn0=', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
|
||
{<br>
|
||
"auth": {<br>
|
||
"OPENAI_API_KEY": "sk-proj-test-key123"<br>
|
||
},<br>
|
||
"config": "[model_providers.openai]\nbase_url =
|
||
\"https://api.openai.com/v1\"\n\n[general]\nmodel = \"gpt-5.1\""<br>
|
||
}
|
||
<div style="color: #95a5a6; margin-top: 12px; margin-bottom: 4px;">// config 字段解析 (TOML):</div>
|
||
<div style="color: #a8d08d;">[model_providers.openai]</div>
|
||
<div style="color: #a8d08d;">base_url = "https://api.openai.com/v1"</div>
|
||
<div style="color: #a8d08d; margin-top: 8px;">[general]</div>
|
||
<div style="color: #a8d08d;">model = "gpt-5.1"</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Gemini 配置导入 -->
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-gemini">Gemini</span>
|
||
Gemini 配置导入
|
||
</h3>
|
||
<p class="description">
|
||
Gemini 使用扁平的环境变量格式,简洁明了。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> name, config<br>
|
||
<span class="param-tag optional">可选</span> <strong>configFormat=json</strong>
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini&configFormat=json&config=eyJHRU1JTklfQVBJX0tFWSI6IkFJemFTeUR0ZXN0a2V5MTIzIiwiR0VNSU5JX0JBU0VfVVJMIjoiaHR0cHM6Ly9nZW5lcmF0aXZlbGFuZ3VhZ2UuZ29vZ2xlYXBpcy5jb20vdjFiZXRhIiwiR0VNSU5JX01PREVMIjoiZ2VtaW5pLTMtcHJvLXByZXZpZXcifQ=="
|
||
class="deep-link">
|
||
📥 导入 Gemini 配置
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=gemini&name=Google%20Gemini&configFormat=json&config=eyJHRU1JTklfQVBJX0tFWSI6IkFJemFTeUR0ZXN0a2V5MTIzIiwiR0VNSU5JX0JBU0VfVVJMIjoiaHR0cHM6Ly9nZW5lcmF0aXZlbGFuZ3VhZ2UuZ29vZ2xlYXBpcy5jb20vdjFiZXRhIiwiR0VNSU5JX01PREVMIjoiZ2VtaW5pLTMtcHJvLXByZXZpZXcifQ==', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
|
||
{<br>
|
||
"GEMINI_API_KEY": "AIzaSyDtestkey123",<br>
|
||
"GEMINI_BASE_URL": "https://generativelanguage.googleapis.com/v1beta",<br>
|
||
"GEMINI_MODEL": "gemini-3-pro-preview"<br>
|
||
}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 自动填充示例 -->
|
||
<div class="link-card">
|
||
<h3>
|
||
<span class="app-badge badge-claude">Claude</span>
|
||
自动填充示例
|
||
</h3>
|
||
<p class="description">
|
||
仅提供配置文件和名称,其他信息(API Key、端点、官网)自动从配置文件中提取。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> name, config<br>
|
||
<span class="param-tag">自动填充</span> apiKey, endpoint, homepage (从配置文件提取)
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Auto%20Filled&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0FVVEhfVE9LRU4iOiJzay1hbnQtYXV0by1maWxsZWQta2V5IiwiQU5USFJPUElDX0JBU0VfVVJMIjoiaHR0cHM6Ly9hcGkuYW50aHJvcGljLmNvbS92MSJ9fQ=="
|
||
class="deep-link">
|
||
📥 测试自动填充
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Auto%20Filled&configFormat=json&config=eyJlbnYiOnsiQU5USFJPUElDX0FVVEhfVE9LRU4iOiJzay1hbnQtYXV0by1maWxsZWQta2V5IiwiQU5USFJPUElDX0JBU0VfVVJMIjoiaHR0cHM6Ly9hcGkuYW50aHJvcGljLmNvbS92MSJ9fQ==', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">// 解码后的配置内容:</div>
|
||
{<br>
|
||
"env": {<br>
|
||
"ANTHROPIC_AUTH_TOKEN": "sk-ant-auto-filled-key",<br>
|
||
"ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1"<br>
|
||
}<br>
|
||
}<br>
|
||
<div style="color: #52c41a; margin-top: 8px;">✓ API Key: 从配置文件提取</div>
|
||
<div style="color: #52c41a;">✓ Endpoint: 从配置文件提取</div>
|
||
<div style="color: #52c41a;">✓ Homepage: 自动推断为 https://anthropic.com</div>
|
||
</div>
|
||
<div
|
||
style="margin-top: 12px; padding: 10px; background: #d1ecf1; border-left: 4px solid #17a2b8; border-radius: 4px; font-size: 13px;">
|
||
<strong>自动填充规则:</strong><br>
|
||
• API Key: 从配置文件中提取<br>
|
||
• Endpoint: 从配置文件中提取<br>
|
||
• Homepage: 根据 Endpoint 自动推断
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 功能说明 -->
|
||
<div
|
||
style="margin-top: 24px; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; color: white;">
|
||
<h4 style="margin-bottom: 16px; font-size: 18px;">✨ 配置文件导入新特性 (v3.8+)</h4>
|
||
<ul style="list-style: none; padding-left: 0;">
|
||
<li style="margin-bottom: 12px; padding-left: 24px; position: relative;">
|
||
<span style="position: absolute; left: 0;">📄</span>
|
||
<strong>支持格式:</strong> JSON 和 TOML 配置文件
|
||
</li>
|
||
<li style="margin-bottom: 12px; padding-left: 24px; position: relative;">
|
||
<span style="position: absolute; left: 0;">🎯</span>
|
||
<strong>优先级规则:</strong> URL 参数 > 配置文件内容 > 远程 URL
|
||
</li>
|
||
<li style="margin-bottom: 12px; padding-left: 24px; position: relative;">
|
||
<span style="position: absolute; left: 0;">🔀</span>
|
||
<strong>智能合并:</strong> URL 参数覆盖配置文件,保留非冲突字段
|
||
</li>
|
||
<li style="margin-bottom: 12px; padding-left: 24px; position: relative;">
|
||
<span style="position: absolute; left: 0;">✨</span>
|
||
<strong>自动填充:</strong> 未提供的字段自动从配置文件提取
|
||
</li>
|
||
<li style="padding-left: 24px; position: relative;">
|
||
<span style="position: absolute; left: 0;">🌐</span>
|
||
<strong>官网推断:</strong> 根据 API 端点自动推断供应商官网
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 供应商导入示例 -->
|
||
<div class="section">
|
||
<h2>🏢 供应商导入 <span class="version-badge">v3.8+</span></h2>
|
||
|
||
<div class="link-card">
|
||
<h3>OpenAI 供应商导入 - 导入并立即切换</h3>
|
||
<p class="description">
|
||
导入 OpenAI 供应商配置,并立即设为当前供应商。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app, name, endpoint, homepage, apiKey<br>
|
||
<span class="param-tag optional">可选</span> model, notes, enabled
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=OpenAI&endpoint=https://api.openai.com/v1/chat/completions&homepage=https://openai.com&apiKey=sk-your-key-here&model=gpt-4&enabled=true"
|
||
class="deep-link">📥 导入 OpenAI (立即切换)</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=OpenAI&endpoint=https://api.openai.com/v1/chat/completions&homepage=https://openai.com&apiKey=sk-your-key-here&model=gpt-4&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">🏢 将导入以下供应商配置:</div>
|
||
<div style="color: #52c41a; margin-bottom: 4px;">• <strong>供应商名称</strong>: OpenAI
|
||
</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>目标应用</strong>: Claude</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>API Endpoint</strong>:
|
||
https://api.openai.com/v1/chat/completions</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>主页</strong>: https://openai.com
|
||
</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>默认模型</strong>: gpt-4</div>
|
||
<div style="color: #ffa07a; margin-bottom: 4px;">• <strong>enabled=true</strong>:
|
||
导入后立即设为当前供应商</div>
|
||
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
💡 <strong>说明</strong>: 导入后会自动切换到此供应商,并同步到 Claude live 配置。<br>
|
||
🔄 <strong>效果</strong>: Claude 将立即使用新导入的供应商进行对话。<br>
|
||
⚠️ <strong>注意</strong>: 请将 <code>sk-your-key-here</code> 替换为您的真实 API Key。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>备用供应商导入 - 仅添加不切换</h3>
|
||
<p class="description">
|
||
导入备用供应商配置,但不立即切换,保留当前供应商。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=provider, app, name, endpoint, homepage, apiKey<br>
|
||
<span class="param-tag optional">可选</span> model, notes, enabled
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=provider&app=claude&name=Backup Provider&endpoint=https://api.backup.com/v1/chat/completions&homepage=https://backup.com&apiKey=sk-backup-key¬es=备用账号&enabled=false"
|
||
class="deep-link">📥 导入备用供应商 (不切换)</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=provider&app=claude&name=Backup Provider&endpoint=https://api.backup.com/v1/chat/completions&homepage=https://backup.com&apiKey=sk-backup-key¬es=备用账号&enabled=false', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">🏢 将导入以下供应商配置:</div>
|
||
<div style="color: #52c41a; margin-bottom: 4px;">• <strong>供应商名称</strong>: Backup Provider
|
||
</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>目标应用</strong>: Claude</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>API Endpoint</strong>:
|
||
https://api.backup.com/v1/chat/completions</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>备注</strong>: 备用账号</div>
|
||
<div style="color: #87ceeb; margin-bottom: 4px;">• <strong>enabled=false</strong>:
|
||
仅添加到列表,不切换</div>
|
||
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
💡 <strong>说明</strong>: 供应商会添加到列表中,但不会影响当前正在使用的供应商。<br>
|
||
🔄 <strong>效果</strong>: Claude 继续使用原有供应商,新供应商可在需要时手动切换。<br>
|
||
📋 <strong>用途</strong>: 适合添加备用供应商、测试新配置等场景。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>enabled 参数对比</h3>
|
||
<p class="description">
|
||
enabled 参数控制导入后是否立即切换到新供应商。
|
||
</p>
|
||
|
||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px;">
|
||
<div style="background: #e8f5e9; padding: 16px; border-radius: 8px; border: 2px solid #4caf50;">
|
||
<h4 style="color: #2e7d32; margin-top: 0;">enabled=true ✅</h4>
|
||
<ul style="margin: 8px 0; padding-left: 20px; color: #2c3e50;">
|
||
<li>立即设为当前供应商</li>
|
||
<li>同步到 live 配置</li>
|
||
<li>同步 MCP 服务器</li>
|
||
<li>Claude 立即使用新供应商</li>
|
||
</ul>
|
||
<div
|
||
style="margin-top: 12px; padding: 8px; background: #fff; border-radius: 4px; font-size: 11px;">
|
||
<strong>适用场景:</strong><br>
|
||
• 首次安装主要供应商<br>
|
||
• 从旧设备迁移配置<br>
|
||
• 快速切换到新供应商
|
||
</div>
|
||
</div>
|
||
|
||
<div style="background: #fff3e0; padding: 16px; border-radius: 8px; border: 2px solid #ff9800;">
|
||
<h4 style="color: #e65100; margin-top: 0;">enabled=false 或省略 ⏸️</h4>
|
||
<ul style="margin: 8px 0; padding-left: 20px; color: #2c3e50;">
|
||
<li>仅添加到供应商列表</li>
|
||
<li>不改变当前供应商</li>
|
||
<li>不影响 live 配置</li>
|
||
<li>可随时手动切换</li>
|
||
</ul>
|
||
<div
|
||
style="margin-top: 12px; padding: 8px; background: #fff; border-radius: 4px; font-size: 11px;">
|
||
<strong>适用场景:</strong><br>
|
||
• 添加备用供应商<br>
|
||
• 批量导入多个配置<br>
|
||
• 测试新供应商设置
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MCP、Prompt、Skill 示例 -->
|
||
<div class="section">
|
||
<h2>🔌 MCP Servers 导入 <span class="version-badge">v3.8+</span></h2>
|
||
|
||
<div class="link-card">
|
||
<h3>批量导入 - Sequential Thinking MCP Server</h3>
|
||
<p class="description">
|
||
导入 Sequential Thinking MCP 服务器,支持结构化思维推理。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=mcp, apps, config (Base64)<br>
|
||
<span class="param-tag optional">可选</span> enabled
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=mcp&apps=claude,codex&config=eyJtY3BTZXJ2ZXJzIjp7InNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwidHlwZSI6InN0ZGlvIn19fQ==&enabled=true"
|
||
class="deep-link">📥 导入到 Claude & Codex</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=mcp&apps=claude,codex&config=eyJtY3BTZXJ2ZXJzIjp7InNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwidHlwZSI6InN0ZGlvIn19fQ==&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">📦 将导入以下 MCP 服务器配置:</div>
|
||
<div style="color: #52c41a; margin-bottom: 4px;">• <strong>服务器名称</strong>: sequential-thinking
|
||
</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>命令</strong>: npx -y
|
||
@modelcontextprotocol/server-sequential-thinking</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>类型</strong>: stdio (标准输入输出)</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>目标应用</strong>: Claude, Codex</div>
|
||
<div style="color: #95a5a6; margin-top: 8px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
💡 <strong>功能说明</strong>: 该 MCP 服务器提供结构化的思维链推理能力,帮助 AI 进行逐步分析和推理。<br>
|
||
🔄 <strong>智能合并</strong>: 如果该 MCP 服务器已存在,导入时将只启用对应应用的开关,不会修改服务器的其他配置。
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>导入到单个应用</h3>
|
||
<p class="description">仅导入到指定的单个应用。</p>
|
||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=mcp&apps=claude&config=eyJtY3BTZXJ2ZXJzIjp7InNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwidHlwZSI6InN0ZGlvIn19fQ==&enabled=true"
|
||
class="deep-link">📥 Claude Only</a>
|
||
<a href="ccswitch://v1/import?resource=mcp&apps=codex&config=eyJtY3BTZXJ2ZXJzIjp7InNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwidHlwZSI6InN0ZGlvIn19fQ==&enabled=true"
|
||
class="deep-link">📥 Codex Only</a>
|
||
<a href="ccswitch://v1/import?resource=mcp&apps=gemini&config=eyJtY3BTZXJ2ZXJzIjp7InNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwidHlwZSI6InN0ZGlvIn19fQ==&enabled=true"
|
||
class="deep-link">📥 Gemini Only</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>📦 JSON 配置示例 - 单个 MCP Server</h3>
|
||
<p class="description">
|
||
使用 JSON 配置导入单个 MCP 服务器 (Context7)。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=mcp, apps, config (Base64)<br>
|
||
<span class="param-tag optional">可选</span> enabled
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=mcp&apps=claude&config=eyJtY3BTZXJ2ZXJzIjp7ImNvbnRleHQ3Ijp7ImNvbW1hbmQiOiJidW54IiwiYXJncyI6WyIteSIsIkB1cHN0YXNoL2NvbnRleHQ3LW1jcCIsIi0tYXBpLWtleSIsImN0eDdzay00ZGRkNGY2Ni1lNzUyLTQwMjItYjFmNi1jOGNmNjI3OWI4MGQiXSwiZW52Ijp7fX19fQ==&enabled=true"
|
||
class="deep-link">📥 导入 Context7</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=mcp&apps=claude&config=eyJtY3BTZXJ2ZXJzIjp7ImNvbnRleHQ3Ijp7ImNvbW1hbmQiOiJidW54IiwiYXJncyI6WyIteSIsIkB1cHN0YXNoL2NvbnRleHQ3LW1jcCIsIi0tYXBpLWtleSIsImN0eDdzay00ZGRkNGY2Ni1lNzUyLTQwMjItYjFmNi1jOGNmNjI3OWI4MGQiXSwiZW52Ijp7fX19fQ==&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- JSON 配置展示 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">📦 MCP 配置 JSON:</div>
|
||
<pre style="margin: 0; color: #a8d08d; line-height: 1.6;">{
|
||
"mcpServers": {
|
||
"context7": {
|
||
"command": "bunx",
|
||
"args": [
|
||
"-y",
|
||
"@upstash/context7-mcp",
|
||
"--api-key",
|
||
"ctx7sk-4ddd4f66-e752-4022-b1f6-c8cf6279b80d"
|
||
],
|
||
"env": {}
|
||
}
|
||
}
|
||
}</pre>
|
||
<div style="color: #95a5a6; margin-top: 8px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
💡 <strong>说明</strong>: Context7 是一个 MCP 服务器,需要 API Key。<br>
|
||
🔧 <strong>命令</strong>: 使用 bunx 运行 @upstash/context7-mcp<br>
|
||
🎯 <strong>目标应用</strong>: Claude
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3>📦📦 JSON 配置示例 - 批量导入多个 MCP Servers</h3>
|
||
<p class="description">
|
||
一次性导入多个 MCP 服务器 (Context7 + Sequential-thinking)。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=mcp, apps, config (Base64)<br>
|
||
<span class="param-tag optional">可选</span> enabled
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=mcp&apps=claude,codex&config=eyJtY3BTZXJ2ZXJzIjp7ImNvbnRleHQ3Ijp7ImNvbW1hbmQiOiJidW54IiwiYXJncyI6WyIteSIsIkB1cHN0YXNoL2NvbnRleHQ3LW1jcCIsIi0tYXBpLWtleSIsImN0eDdzay00ZGRkNGY2Ni1lNzUyLTQwMjItYjFmNi1jOGNmNjI3OWI4MGQiXSwiZW52Ijp7fX0sInNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwiZW52Ijp7fX19fQ==&enabled=true"
|
||
class="deep-link">📥 批量导入 2 个 MCP Servers</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=mcp&apps=claude,codex&config=eyJtY3BTZXJ2ZXJzIjp7ImNvbnRleHQ3Ijp7ImNvbW1hbmQiOiJidW54IiwiYXJncyI6WyIteSIsIkB1cHN0YXNoL2NvbnRleHQ3LW1jcCIsIi0tYXBpLWtleSIsImN0eDdzay00ZGRkNGY2Ni1lNzUyLTQwMjItYjFmNi1jOGNmNjI3OWI4MGQiXSwiZW52Ijp7fX0sInNlcXVlbnRpYWwtdGhpbmtpbmciOnsiY29tbWFuZCI6Im5weCIsImFyZ3MiOlsiLXkiLCJAbW9kZWxjb250ZXh0cHJvdG9jb2wvc2VydmVyLXNlcXVlbnRpYWwtdGhpbmtpbmciXSwiZW52Ijp7fX19fQ==&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- JSON 配置展示 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">📦 批量 MCP 配置 JSON:</div>
|
||
<pre style="margin: 0; color: #a8d08d; line-height: 1.6;">{
|
||
"mcpServers": {
|
||
"context7": {
|
||
"command": "bunx",
|
||
"args": [
|
||
"-y",
|
||
"@upstash/context7-mcp",
|
||
"--api-key",
|
||
"ctx7sk-4ddd4f66-e752-4022-b1f6-c8cf6279b80d"
|
||
],
|
||
"env": {}
|
||
},
|
||
"sequential-thinking": {
|
||
"command": "npx",
|
||
"args": [
|
||
"-y",
|
||
"@modelcontextprotocol/server-sequential-thinking"
|
||
],
|
||
"env": {}
|
||
}
|
||
}
|
||
}</pre>
|
||
<div style="color: #95a5a6; margin-top: 8px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
💡 <strong>批量导入说明</strong>: 一次性导入 2 个 MCP 服务器<br>
|
||
📦 <strong>服务器 1</strong>: context7 - Upstash Context7 MCP 服务器<br>
|
||
📦 <strong>服务器 2</strong>: sequential-thinking - 结构化思维推理服务器<br>
|
||
🎯 <strong>目标应用</strong>: Claude 和 Codex<br>
|
||
🔄 <strong>智能合并</strong>: 如果服务器已存在,只更新应用启用状态,不覆盖配置
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Prompt 导入示例 -->
|
||
<div class="section">
|
||
<h2>💬 Prompt 导入 <span class="version-badge">v3.8+</span></h2>
|
||
|
||
<div class="link-card">
|
||
<h3><span class="app-badge badge-claude">Claude</span> 代码审查专家</h3>
|
||
<p class="description">为 Claude 导入代码审查提示词。</p>
|
||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=prompt&app=claude&name=代码审查专家&content=IyDku6PnoIHlrqHmn6XkuJPlrrYKCuS9oOaYr+S4gOS9jeaciee7j+mqjOeahOS7o+eggeWuoeafpeWRmO+8jOivt+WcqOS7o+eggeWuoeafpeWbnuWkjeeahOaXtuWAmeWBmuWQr+S4i+OAgg==&description=专注代码质量&enabled=true"
|
||
class="deep-link">📥 导入</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=prompt&app=claude&name=代码审查专家&content=IyDku6PnoIHlrqHmn6XkuJPlrrYKCuS9oOaYr+S4gOS9jeaciee7j+mqjOeahOS7o+eggeWuoeafpeWRmO+8jOivt+WcqOS7o+eggeWuoeafpeWbnuWkjeeahOaXtuWAmeWBmuWQr+S4i+OAgg==&description=专注代码质量&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">📝 Prompt 内容:</div>
|
||
<div style="color: #a8d08d; white-space: pre-wrap; line-height: 1.6;"># 代码审查专家
|
||
|
||
你是一位有经验的代码审查员,请在代码审查回复的时候做启下。</div>
|
||
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
• <strong>应用</strong>: Claude<br>
|
||
• <strong>描述</strong>: 专注代码质量<br>
|
||
• <strong>状态</strong>: 导入后立即启用
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3><span class="app-badge badge-codex">Codex</span> 代码优化助手</h3>
|
||
<p class="description">为 Codex 导入代码优化提示词。</p>
|
||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=prompt&app=codex&name=代码优化助手&content=IyDku6PnoIHkvJjljJbliqnmiYsKCuS9oOaYr+S4gOS9jeS7o+eggeS8mOWMluS4k+S4mueahOWKqeaJi+OAgg==&description=提升代码性能&enabled=true"
|
||
class="deep-link">📥 导入</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=prompt&app=codex&name=代码优化助手&content=IyDku6PnoIHkvJjljJbliqnmiYsKCuS9oOaYr+S4gOS9jeS7o+eggeS8mOWMluS4k+S4mueahOWKqeaJi+OAgg==&description=提升代码性能&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">📝 Prompt 内容:</div>
|
||
<div style="color: #a8d08d; white-space: pre-wrap; line-height: 1.6;"># 代码优化助手
|
||
|
||
你是一位代码优化专业的助手。</div>
|
||
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
• <strong>应用</strong>: Codex<br>
|
||
• <strong>描述</strong>: 提升代码性能<br>
|
||
• <strong>状态</strong>: 导入后立即启用
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="link-card">
|
||
<h3><span class="app-badge badge-gemini">Gemini</span> 架构设计师</h3>
|
||
<p class="description">为 Gemini 导入架构设计提示词。</p>
|
||
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=prompt&app=gemini&name=架构设计师&content=IyDmnrbmnoTorr7orqHluIgKCuS9oOaYr+S4k+S4muaetuaehOiuvuiuoeS4k+S4mueahOS4k+WutuOAgg==&description=系统架构专家&enabled=true"
|
||
class="deep-link">📥 导入</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=prompt&app=gemini&name=架构设计师&content=IyDmnrbmnoTorr7orqHluIgKCuS9oOaYr+S4k+S4muaetuaehOiuvuiuoeS4k+S4mueahOS4k+WutuOAgg==&description=系统架构专家&enabled=true', this)">📋
|
||
复制</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">📝 Prompt 内容:</div>
|
||
<div style="color: #a8d08d; white-space: pre-wrap; line-height: 1.6;"># 架构设计师
|
||
|
||
你是专业架构设计专业的专家。</div>
|
||
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
• <strong>应用</strong>: Gemini<br>
|
||
• <strong>描述</strong>: 系统架构专家<br>
|
||
• <strong>状态</strong>: 导入后立即启用
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Skill 导入示例 -->
|
||
<div class="section">
|
||
<h2>🛠️ Skill 仓库导入 <span class="version-badge">v3.8+</span></h2>
|
||
|
||
<div class="link-card">
|
||
<h3>添加 Claude Skill 仓库</h3>
|
||
<p class="description">
|
||
从 GitHub 仓库导入 Claude Skills,支持指定分支和子目录路径。
|
||
</p>
|
||
<div class="param-list">
|
||
<span class="param-tag">必需</span> resource=skill, repo (owner/name)<br>
|
||
<span class="param-tag optional">可选</span> branch, skills_path, directory
|
||
</div>
|
||
<div style="display: flex; gap: 10px; align-items: center; flex-wrap: wrap;">
|
||
<a href="ccswitch://v1/import?resource=skill&repo=example/claude-skills&branch=main&skills_path=skills&directory=my-skills"
|
||
class="deep-link">
|
||
📥 导入 Skill 仓库示例
|
||
</a>
|
||
<button class="deep-link"
|
||
style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%); cursor: pointer; border: none;"
|
||
onclick="copyDeepLink('ccswitch://v1/import?resource=skill&repo=example/claude-skills&branch=main&skills_path=skills&directory=my-skills', this)">
|
||
📋 复制链接
|
||
</button>
|
||
</div>
|
||
|
||
<!-- 内容解释 -->
|
||
<div class="code-block"
|
||
style="margin-top: 12px; padding: 12px; background: #2c3e50; color: #ecf0f1; border-radius: 8px; font-family: monospace; font-size: 12px; overflow-x: auto;">
|
||
<div style="color: #95a5a6; margin-bottom: 8px;">🗂️ 将添加以下 Skill 仓库:</div>
|
||
<div style="color: #52c41a; margin-bottom: 4px;">• <strong>GitHub 仓库</strong>:
|
||
example/claude-skills</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>分支</strong>: main (默认分支)</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>Skills 路径</strong>: skills
|
||
(仓库中技能文件所在的子目录)</div>
|
||
<div style="color: #a8d08d; margin-bottom: 4px;">• <strong>本地目录</strong>: my-skills (克隆到本地的目录名)
|
||
</div>
|
||
<div style="color: #95a5a6; margin-top: 12px; padding-top: 8px; border-top: 1px solid #34495e;">
|
||
💡 <strong>说明</strong>: 此操作会把仓库添加到 Skill 列表中。添加后,您可以在 Skills 管理界面选择安装具体的技能文件。<br>
|
||
🔧 <strong>应用</strong>: Claude (Skills 功能仅支持 Claude)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 深链接生成器 -->
|
||
<div class="section">
|
||
<h2>🚀 深链接生成器</h2>
|
||
<p style="color: #7f8c8d; margin-bottom: 24px;">
|
||
填写参数信息,自动生成深链接并导入到 CC Switch
|
||
</p>
|
||
|
||
<!-- 供应商生成器 -->
|
||
<div class="generator-section">
|
||
<h3 style="color: #2c3e50; margin-bottom: 16px;">🏢 供应商导入生成器</h3>
|
||
|
||
<div class="form-group">
|
||
<label>目标应用 *</label>
|
||
<select id="providerApp">
|
||
<option value="claude">Claude</option>
|
||
<option value="codex">Codex</option>
|
||
<option value="gemini">Gemini</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>供应商名称 *</label>
|
||
<input type="text" id="providerName" placeholder="例如: OpenAI" />
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>API Key *</label>
|
||
<input type="text" id="providerApiKey" placeholder="sk-..." />
|
||
<small style="color: #7f8c8d;">您的 API 密钥</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>API Endpoint *</label>
|
||
<input type="text" id="providerEndpoint"
|
||
placeholder="https://api.openai.com/v1/chat/completions" />
|
||
<small style="color: #7f8c8d;">完整的 API 端点 URL</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>主页链接 *</label>
|
||
<input type="text" id="providerHomepage" placeholder="https://openai.com" />
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>默认模型</label>
|
||
<input type="text" id="providerModel" placeholder="gpt-4" />
|
||
<small style="color: #7f8c8d;">可选,留空使用系统默认</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>备注</label>
|
||
<input type="text" id="providerNotes" placeholder="例如: 个人账号" />
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>导入后是否设为当前供应商</label>
|
||
<select id="providerEnabled">
|
||
<option value="true">是 (立即切换到此供应商)</option>
|
||
<option value="false">否 (仅添加,不切换)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<button class="btn" onclick="generateProviderLink()">🎯 生成供应商深链接</button>
|
||
|
||
<div id="providerResult" class="result-section" style="display: none;">
|
||
<label>生成的深链接:</label>
|
||
<div class="result-url" id="providerUrl" onclick="selectText(this)"></div>
|
||
<div style="display: flex; gap: 10px; margin-top: 12px;">
|
||
<button class="btn" onclick="copyGeneratedLink('providerUrl')">📋 复制链接</button>
|
||
<a id="providerImportBtn" class="btn"
|
||
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
|
||
📥 立即导入
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- MCP Servers 生成器 -->
|
||
<div class="generator-section">
|
||
<h3 style="color: #2c3e50; margin-bottom: 16px;">🔌 MCP Servers 导入生成器</h3>
|
||
|
||
<div class="form-group">
|
||
<label>目标应用 *</label>
|
||
<input type="text" id="mcpApps" placeholder="例如: claude,codex,gemini 或 claude" />
|
||
<small style="color: #7f8c8d;">多个应用用逗号分隔</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>MCP 配置 (JSON) *</label>
|
||
<textarea id="mcpConfig" rows="8" placeholder='{
|
||
"mcpServers": {
|
||
"server-name": {
|
||
"command": "npx",
|
||
"args": ["-y", "@modelcontextprotocol/server-xxx"],
|
||
"type": "stdio"
|
||
}
|
||
}
|
||
}'></textarea>
|
||
<small style="color: #7f8c8d;">完整的 MCP 配置 JSON</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>是否启用</label>
|
||
<select id="mcpEnabled">
|
||
<option value="true">是 (enabled=true)</option>
|
||
<option value="false">否 (enabled=false)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<button class="btn" onclick="generateMcpLink()">🎯 生成 MCP 深链接</button>
|
||
|
||
<div id="mcpResult" class="result-section" style="display: none;">
|
||
<label>生成的深链接:</label>
|
||
<div class="result-url" id="mcpUrl" onclick="selectText(this)"></div>
|
||
<div style="display: flex; gap: 10px; margin-top: 12px;">
|
||
<button class="btn" onclick="copyGeneratedLink('mcpUrl')">📋 复制链接</button>
|
||
<a id="mcpImportBtn" class="btn"
|
||
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
|
||
📥 立即导入
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Prompt 生成器 -->
|
||
<div class="generator-section">
|
||
<h3 style="color: #2c3e50; margin-bottom: 16px;">💬 Prompt 导入生成器</h3>
|
||
|
||
<div class="form-group">
|
||
<label>目标应用 *</label>
|
||
<select id="promptApp">
|
||
<option value="claude">Claude</option>
|
||
<option value="codex">Codex</option>
|
||
<option value="gemini">Gemini</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>提示词名称 *</label>
|
||
<input type="text" id="promptName" placeholder="例如: 代码审查专家" />
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>提示词内容 *</label>
|
||
<textarea id="promptContent" rows="6" placeholder="# 角色定义
|
||
|
||
你是一位专业的..."></textarea>
|
||
<small style="color: #7f8c8d;">支持 Markdown 格式,自动 Base64 编码</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>描述</label>
|
||
<input type="text" id="promptDescription" placeholder="例如: 专注代码质量" />
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>导入后是否启用</label>
|
||
<select id="promptEnabled">
|
||
<option value="true">是 (将禁用其他提示词)</option>
|
||
<option value="false">否 (保持禁用状态)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<button class="btn" onclick="generatePromptLink()">🎯 生成 Prompt 深链接</button>
|
||
|
||
<div id="promptResult" class="result-section" style="display: none;">
|
||
<label>生成的深链接:</label>
|
||
<div class="result-url" id="promptUrl" onclick="selectText(this)"></div>
|
||
<div style="display: flex; gap: 10px; margin-top: 12px;">
|
||
<button class="btn" onclick="copyGeneratedLink('promptUrl')">📋 复制链接</button>
|
||
<a id="promptImportBtn" class="btn"
|
||
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
|
||
📥 立即导入
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Skill 生成器 -->
|
||
<div class="generator-section">
|
||
<h3 style="color: #2c3e50; margin-bottom: 16px;">🛠️ Skill 仓库导入生成器</h3>
|
||
|
||
<div class="form-group">
|
||
<label>GitHub 仓库 *</label>
|
||
<input type="text" id="skillRepo" placeholder="例如: owner/repo-name" />
|
||
<small style="color: #7f8c8d;">格式: 所有者/仓库名</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>分支</label>
|
||
<input type="text" id="skillBranch" placeholder="main" value="main" />
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Skills 路径</label>
|
||
<input type="text" id="skillPath" placeholder="skills" value="skills" />
|
||
<small style="color: #7f8c8d;">仓库中技能文件所在的子目录</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>本地目录名</label>
|
||
<input type="text" id="skillDirectory" placeholder="my-skills" />
|
||
<small style="color: #7f8c8d;">克隆到本地的目录名(可选)</small>
|
||
</div>
|
||
|
||
<button class="btn" onclick="generateSkillLink()">🎯 生成 Skill 深链接</button>
|
||
|
||
<div id="skillResult" class="result-section" style="display: none;">
|
||
<label>生成的深链接:</label>
|
||
<div class="result-url" id="skillUrl" onclick="selectText(this)"></div>
|
||
<div style="display: flex; gap: 10px; margin-top: 12px;">
|
||
<button class="btn" onclick="copyGeneratedLink('skillUrl')">📋 复制链接</button>
|
||
<a id="skillImportBtn" class="btn"
|
||
style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%); text-decoration: none;">
|
||
📥 立即导入
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Base64 编解码器 -->
|
||
<div class="section">
|
||
<h2>🔐 Base64 编解码器</h2>
|
||
|
||
<div class="generator-section" style="background: #f5f5f5; border: 2px solid #95a5a6;">
|
||
<h3 style="color: #2c3e50; margin-bottom: 16px;">编码器 (UTF-8 → Base64)</h3>
|
||
<div class="form-group">
|
||
<label>原始内容(UTF-8 文本)</label>
|
||
<textarea id="encodeInput" rows="6" placeholder="输入要编码的文本... 支持中文、JSON、TOML 等所有 UTF-8 字符"
|
||
style="font-family: monospace; font-size: 13px;"></textarea>
|
||
</div>
|
||
<button class="btn" style="background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);"
|
||
onclick="encodeToBase64()">
|
||
🔒 编码为 Base64
|
||
</button>
|
||
<div id="encodeResult" style="display: none;" class="result-box">
|
||
<strong>✅ 编码结果:</strong>
|
||
<div class="result-text" id="encodeOutput"></div>
|
||
<button class="btn btn-copy" onclick="copyEncoded()">📋 复制结果</button>
|
||
<small style="color: #7f8c8d; display: block; margin-top: 8px;">
|
||
💡 可直接用于深链接的 config 或 content 参数
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="generator-section"
|
||
style="background: #f0f9ff; border: 2px solid #3498db; margin-top: 24px;">
|
||
<h3 style="color: #2c3e50; margin-bottom: 16px;">解码器 (Base64 → UTF-8)</h3>
|
||
<div class="form-group">
|
||
<label>Base64 编码内容</label>
|
||
<textarea id="decodeInput" rows="6"
|
||
placeholder="粘贴 Base64 编码的文本... 例如: eyJlbnYiOnsiaGVsbG8iOiJ3b3JsZCJ9fQ=="
|
||
style="font-family: monospace; font-size: 13px;"></textarea>
|
||
</div>
|
||
<button class="btn" style="background: linear-gradient(135deg, #27ae60 0%, #229954 100%);"
|
||
onclick="decodeFromBase64()">
|
||
🔓 解码为文本
|
||
</button>
|
||
<div id="decodeResult" style="display: none;" class="result-box">
|
||
<strong>✅ 解码结果:</strong>
|
||
<div class="result-text" id="decodeOutput" style="white-space: pre-wrap;"></div>
|
||
<button class="btn btn-copy" onclick="copyDecoded()">📋 复制结果</button>
|
||
<div id="jsonFormat" style="display: none; margin-top: 12px;">
|
||
<button class="btn" style="background: linear-gradient(135deg, #9b59b6 0%, #8e44ad 100%);"
|
||
onclick="formatJson()">
|
||
✨ 格式化 JSON
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="info-box" style="margin-top: 24px; background: #e8f4f8; border-left: 4px solid #3498db;">
|
||
<h4 style="color: #2c3e50;">💡 使用建议</h4>
|
||
<ul style="color: #2c3e50;">
|
||
<li><strong>编码配置文件</strong>:将 JSON 或 TOML 内容编码后用于 config 参数</li>
|
||
<li><strong>编码 Prompt</strong>:将 Markdown 提示词内容编码后用于 content 参数</li>
|
||
<li><strong>验证深链接</strong>:解码验证深链接中的配置内容是否正确</li>
|
||
<li><strong>UTF-8 支持</strong>:完整支持中文及其他 Unicode 字符</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 注意事项 -->
|
||
<div class="info-box">
|
||
<h4>⚠️ 使用注意事项</h4>
|
||
<ul>
|
||
<li><strong>首次点击</strong>:浏览器会询问是否允许打开 CC Switch,请点击"允许"或"打开"</li>
|
||
<li><strong>macOS 用户</strong>:可能需要在"系统设置" → "隐私与安全性"中允许应用</li>
|
||
<li><strong>测试 API Key</strong>:示例中的 API Key 仅用于测试格式,无法实际使用</li>
|
||
<li><strong>导入确认</strong>:点击链接后会弹出确认对话框,API Key 会被掩码显示(前4位+****)</li>
|
||
<li><strong>编辑配置</strong>:导入后可以在 CC Switch 中随时编辑或删除配置</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- 深链接解析器 -->
|
||
<div class="generator-section" style="background: #f0f9ff; border: 2px solid #3498db;">
|
||
<h2>🔍 深链接解析器</h2>
|
||
<p style="color: #7f8c8d; margin-bottom: 24px;">粘贴深链接 URL,查看解析结果</p>
|
||
|
||
<div class="form-group">
|
||
<label>深链接 URL</label>
|
||
<textarea id="parseUrl" rows="3" placeholder="粘贴完整的 ccswitch:// 深链接..."
|
||
style="font-family: monospace; font-size: 13px;"></textarea>
|
||
</div>
|
||
|
||
<button class="btn" style="background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);"
|
||
onclick="parseDeepLink()">
|
||
🔍 解析深链接
|
||
</button>
|
||
|
||
<!-- 解析结果 -->
|
||
<div id="parseResult" style="display: none;">
|
||
<div class="result-box" style="margin-top: 20px;">
|
||
<strong>✅ 解析结果:</strong>
|
||
|
||
<!-- 基本信息 -->
|
||
<div id="parseBasicInfo" style="margin-top: 16px;"></div>
|
||
|
||
<!-- URL 参数 -->
|
||
<div id="parseUrlParams" style="margin-top: 16px;"></div>
|
||
|
||
<!-- 配置文件解析 -->
|
||
<div id="parseConfigContent" style="margin-top: 16px;"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 深链接生成器 -->
|
||
<div class="generator-section">
|
||
<h2>🛠️ 深链接生成器</h2>
|
||
<p style="color: #7f8c8d; margin-bottom: 24px;">填写下方表单,生成您自己的深链接</p>
|
||
|
||
<!-- 导入模式切换 -->
|
||
<div class="form-group">
|
||
<label>导入模式</label>
|
||
<select id="importMode" onchange="toggleImportMode()">
|
||
<option value="url">URL 参数模式(传统)</option>
|
||
<option value="config">配置文件模式(v3.8+)</option>
|
||
</select>
|
||
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
|
||
URL 参数模式:直接在 URL 中传递参数 | 配置文件模式:使用 Base64 编码的 JSON/TOML
|
||
</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>应用类型 *</label>
|
||
<select id="app" onchange="updateModelFields()">
|
||
<option value="claude">Claude Code</option>
|
||
<option value="codex">Codex</option>
|
||
<option value="gemini">Gemini</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>供应商名称 *</label>
|
||
<input type="text" id="name" placeholder="例如: Claude Official">
|
||
<small style="color: #e74c3c; font-size: 12px; display: block; margin-top: 4px;">
|
||
⚠️ 唯一必填项
|
||
</small>
|
||
</div>
|
||
|
||
<!-- URL 参数模式字段 -->
|
||
<div id="urlModeFields">
|
||
<div class="form-group">
|
||
<label>官网地址</label>
|
||
<input type="url" id="homepage" placeholder="https://example.com(可选,配置文件模式可自动推断)">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>API 端点</label>
|
||
<input type="url" id="endpoint" placeholder="https://api.example.com/v1(配置文件模式可从配置提取)">
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>API Key</label>
|
||
<input type="text" id="apiKey" placeholder="sk-xxxxx 或 AIzaSyXXXXX(配置文件模式可从配置提取)">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 配置文件模式字段 -->
|
||
<div id="configModeFields" style="display: none;">
|
||
<!-- 通用/Claude/Gemini JSON 配置 -->
|
||
<div id="generalConfigGroup" class="form-group">
|
||
<label id="configJsonLabel">配置文件内容(JSON)</label>
|
||
<textarea id="configJson" rows="12"
|
||
placeholder='输入 JSON 配置,例如: { "env": { "ANTHROPIC_AUTH_TOKEN": "sk-ant-xxx", "ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1", "ANTHROPIC_MODEL": "claude-sonnet-4.5" } }'></textarea>
|
||
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
|
||
配置文件将自动进行 Base64 编码
|
||
</small>
|
||
</div>
|
||
|
||
<!-- Codex 专用配置字段 -->
|
||
<div id="codexConfigGroup" style="display: none;">
|
||
<div class="form-group">
|
||
<label>Codex 认证信息 (JSON)</label>
|
||
<textarea id="codexAuthJson" rows="5"
|
||
placeholder='{ "auth": { "OPENAI_API_KEY": "sk-..." } }'></textarea>
|
||
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
|
||
包含 API Key 的认证信息 JSON 对象
|
||
</small>
|
||
</div>
|
||
<div class="form-group">
|
||
<label>Codex 配置文件 (TOML)</label>
|
||
<textarea id="codexConfigToml" rows="10"
|
||
placeholder='[model_providers.openai] base_url = "..." [general] model = "..."'
|
||
style="font-family: monospace;"></textarea>
|
||
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
|
||
config.toml 的原始内容
|
||
</small>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>URL 参数覆盖(可选)</label>
|
||
<div style="background: #fff3cd; padding: 12px; border-radius: 8px; margin-bottom: 8px;">
|
||
<p style="font-size: 12px; color: #856404; margin: 0;">
|
||
💡 可以在下方填写 API Key、端点等参数来覆盖配置文件中的值。留空则完全使用配置文件。
|
||
</p>
|
||
</div>
|
||
<input type="text" id="overrideApiKey" placeholder="覆盖配置文件中的 API Key(可选)">
|
||
<input type="url" id="overrideEndpoint" placeholder="覆盖配置文件中的端点(可选)" style="margin-top: 8px;">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>默认模型(可选)</label>
|
||
<input type="text" id="model" placeholder="例如: claude-haiku-4.1, gpt-5.1, gemini-3-pro-preview">
|
||
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
|
||
通用模型字段,适用于所有应用类型
|
||
</small>
|
||
</div>
|
||
|
||
<!-- Claude 专用字段 -->
|
||
<div id="claudeFields" style="display: block;">
|
||
<div style="background: #e8f4f8; padding: 16px; border-radius: 8px; margin: 16px 0;">
|
||
<h4 style="color: #2c3e50; margin-bottom: 12px; font-size: 14px;">
|
||
📋 Claude 专用模型字段(可选)
|
||
</h4>
|
||
<p style="color: #7f8c8d; font-size: 12px; margin-bottom: 12px;">
|
||
可以根据需要设置特定的模型字段,这些字段仅在 Claude 应用中生效
|
||
</p>
|
||
|
||
<div class="form-group">
|
||
<label>Haiku 模型</label>
|
||
<input type="text" id="haikuModel" placeholder="claude-haiku-4.1">
|
||
<small style="color: #7f8c8d; font-size: 11px; display: block; margin-top: 4px;">
|
||
对应环境变量:ANTHROPIC_DEFAULT_HAIKU_MODEL
|
||
</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Sonnet 模型</label>
|
||
<input type="text" id="sonnetModel" placeholder="claude-sonnet-4.5">
|
||
<small style="color: #7f8c8d; font-size: 11px; display: block; margin-top: 4px;">
|
||
对应环境变量:ANTHROPIC_DEFAULT_SONNET_MODEL
|
||
</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>Opus 模型</label>
|
||
<input type="text" id="opusModel" placeholder="claude-opus-4">
|
||
<small style="color: #7f8c8d; font-size: 11px; display: block; margin-top: 4px;">
|
||
对应环境变量:ANTHROPIC_DEFAULT_OPUS_MODEL
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>图标(可选)</label>
|
||
<input type="text" id="icon" placeholder="例如: openai, anthropic, google">
|
||
<small style="color: #7f8c8d; font-size: 12px; display: block; margin-top: 4px;">
|
||
图标名称,用于在界面中显示
|
||
</small>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>导入后是否设为当前供应商</label>
|
||
<select id="enabled">
|
||
<option value="true">是 (立即切换到此供应商)</option>
|
||
<option value="false">否 (仅添加,不切换)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group">
|
||
<label>备注(可选)</label>
|
||
<textarea id="notes" rows="2" placeholder="例如: 公司专用账号"></textarea>
|
||
</div>
|
||
|
||
<button class="btn" onclick="generateLink()">🚀 生成深链接</button>
|
||
|
||
<div id="result" style="display: none;">
|
||
<div class="result-box">
|
||
<strong>✅ 生成的深链接:</strong>
|
||
<div class="result-text" id="linkText"></div>
|
||
<button class="btn btn-copy" onclick="copyLink()">📋 复制链接</button>
|
||
<a id="testLink" class="deep-link" style="text-decoration: none;">
|
||
🧪 测试链接
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
// UTF-8 字符串转 Base64
|
||
function utf8_to_b64(str) {
|
||
try {
|
||
const bytes = new TextEncoder().encode(str);
|
||
const binString = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
||
return btoa(binString);
|
||
} catch (e) {
|
||
console.error("Base64 encode error:", e);
|
||
return window.btoa(unescape(encodeURIComponent(str)));
|
||
}
|
||
}
|
||
|
||
// Base64 转 UTF-8 字符串
|
||
function b64_to_utf8(str) {
|
||
try {
|
||
const binString = atob(str);
|
||
const bytes = Uint8Array.from(binString, (m) => m.codePointAt(0));
|
||
return new TextDecoder().decode(bytes);
|
||
} catch (e) {
|
||
console.error("Base64 decode error:", e);
|
||
return decodeURIComponent(escape(window.atob(str)));
|
||
}
|
||
}
|
||
|
||
// 切换导入模式
|
||
function toggleImportMode() {
|
||
const mode = document.getElementById('importMode').value;
|
||
const urlFields = document.getElementById('urlModeFields');
|
||
const configFields = document.getElementById('configModeFields');
|
||
|
||
if (mode === 'url') {
|
||
urlFields.style.display = 'block';
|
||
configFields.style.display = 'none';
|
||
} else {
|
||
urlFields.style.display = 'none';
|
||
configFields.style.display = 'block';
|
||
// 当切换到配置文件模式时,自动填充示例配置
|
||
populateConfigTemplate();
|
||
}
|
||
}
|
||
|
||
// ... (rest of the functions) ...
|
||
|
||
// 根据应用类型填充配置模板
|
||
function populateConfigTemplate() {
|
||
const app = document.getElementById('app').value;
|
||
const configTextarea = document.getElementById('configJson');
|
||
const codexAuthTextarea = document.getElementById('codexAuthJson');
|
||
const codexConfigTextarea = document.getElementById('codexConfigToml');
|
||
|
||
let template = '';
|
||
|
||
if (app === 'claude') {
|
||
template = `{
|
||
"env": {
|
||
"ANTHROPIC_AUTH_TOKEN": "sk-ant-your-api-key-here",
|
||
"ANTHROPIC_BASE_URL": "https://api.anthropic.com/v1",
|
||
"ANTHROPIC_MODEL": "claude-sonnet-4.5",
|
||
"ANTHROPIC_DEFAULT_HAIKU_MODEL": "claude-haiku-4.1",
|
||
"ANTHROPIC_DEFAULT_SONNET_MODEL": "claude-sonnet-4.5",
|
||
"ANTHROPIC_DEFAULT_OPUS_MODEL": "claude-opus-4"
|
||
}
|
||
}`;
|
||
configTextarea.value = template;
|
||
} else if (app === 'codex') {
|
||
// Codex 分开填充
|
||
codexAuthTextarea.value = `{
|
||
"auth": {
|
||
"OPENAI_API_KEY": "sk-proj-your-api-key-here"
|
||
}
|
||
}`;
|
||
codexConfigTextarea.value = `model_provider = "custom"
|
||
model = "gpt-5.1"
|
||
model_reasoning_effort = "high"
|
||
disable_response_storage = true
|
||
|
||
[model_providers.custom]
|
||
name = "custom"
|
||
base_url = "https://api.openai.com/v1"
|
||
wire_api = "responses"
|
||
requires_openai_auth = true`;
|
||
} else if (app === 'gemini') {
|
||
template = `{
|
||
"GEMINI_API_KEY": "AIzaSy-your-api-key-here",
|
||
"GEMINI_BASE_URL": "https://generativelanguage.googleapis.com/v1beta",
|
||
"GEMINI_MODEL": "gemini-3-pro-preview"
|
||
}`;
|
||
configTextarea.value = template;
|
||
}
|
||
}
|
||
|
||
// 更新模型字段显示
|
||
function updateModelFields() {
|
||
const app = document.getElementById('app').value;
|
||
const claudeFields = document.getElementById('claudeFields');
|
||
const generalConfigGroup = document.getElementById('generalConfigGroup');
|
||
const codexConfigGroup = document.getElementById('codexConfigGroup');
|
||
const mode = document.getElementById('importMode').value;
|
||
|
||
// Claude 字段显示控制
|
||
if (app === 'claude') {
|
||
claudeFields.style.display = 'block';
|
||
} else {
|
||
claudeFields.style.display = 'none';
|
||
}
|
||
|
||
// 配置文件输入框控制
|
||
if (mode === 'config') {
|
||
if (app === 'codex') {
|
||
generalConfigGroup.style.display = 'none';
|
||
codexConfigGroup.style.display = 'block';
|
||
} else {
|
||
generalConfigGroup.style.display = 'block';
|
||
codexConfigGroup.style.display = 'none';
|
||
}
|
||
populateConfigTemplate();
|
||
}
|
||
}
|
||
|
||
function generateLink() {
|
||
const mode = document.getElementById('importMode').value;
|
||
const app = document.getElementById('app').value;
|
||
const name = document.getElementById('name').value.trim();
|
||
|
||
// 验证必填字段(只有名称是必填的)
|
||
if (!name) {
|
||
alert('❌ 请填写供应商名称!');
|
||
return;
|
||
}
|
||
|
||
// 构建基础参数
|
||
const params = new URLSearchParams({
|
||
resource: 'provider',
|
||
app: app,
|
||
name: name
|
||
});
|
||
|
||
if (mode === 'url') {
|
||
// URL 参数模式
|
||
const homepage = document.getElementById('homepage').value.trim();
|
||
const endpoint = document.getElementById('endpoint').value.trim();
|
||
const apiKey = document.getElementById('apiKey').value.trim();
|
||
const model = document.getElementById('model').value.trim();
|
||
const icon = document.getElementById('icon').value.trim();
|
||
const enabled = document.getElementById('enabled').value;
|
||
const notes = document.getElementById('notes').value.trim();
|
||
|
||
// Claude 专用字段
|
||
const haikuModel = document.getElementById('haikuModel').value.trim();
|
||
const sonnetModel = document.getElementById('sonnetModel').value.trim();
|
||
const opusModel = document.getElementById('opusModel').value.trim();
|
||
|
||
// URL 模式下,至少需要 endpoint 和 apiKey
|
||
if (!endpoint || !apiKey) {
|
||
alert('❌ URL 参数模式下,端点和 API Key 是必填的!');
|
||
return;
|
||
}
|
||
|
||
// 验证 URL 格式
|
||
if (homepage) {
|
||
try {
|
||
new URL(homepage);
|
||
} catch (e) {
|
||
alert('❌ 请输入有效的官网 URL 格式(需包含 http:// 或 https://)!');
|
||
return;
|
||
}
|
||
}
|
||
|
||
try {
|
||
new URL(endpoint);
|
||
} catch (e) {
|
||
alert('❌ 请输入有效的端点 URL 格式(需包含 http:// 或 https://)!');
|
||
return;
|
||
}
|
||
|
||
// 添加参数
|
||
if (homepage) params.append('homepage', homepage);
|
||
params.append('endpoint', endpoint);
|
||
params.append('apiKey', apiKey);
|
||
if (model) params.append('model', model);
|
||
if (icon) params.append('icon', icon);
|
||
if (enabled) params.append('enabled', enabled);
|
||
if (notes) params.append('notes', notes);
|
||
|
||
// 添加 Claude 专用模型字段
|
||
if (app === 'claude') {
|
||
if (haikuModel) params.append('haikuModel', haikuModel);
|
||
if (sonnetModel) params.append('sonnetModel', sonnetModel);
|
||
if (opusModel) params.append('opusModel', opusModel);
|
||
}
|
||
} else {
|
||
// 配置文件模式
|
||
let configJson = '';
|
||
|
||
if (app === 'codex') {
|
||
// Codex 特殊处理:合并 Auth JSON 和 Config TOML
|
||
const authJson = document.getElementById('codexAuthJson').value.trim();
|
||
const configToml = document.getElementById('codexConfigToml').value.trim();
|
||
|
||
if (!authJson) {
|
||
alert('❌ 请填写 Codex 认证信息 (JSON)!');
|
||
return;
|
||
}
|
||
if (!configToml) {
|
||
alert('❌ 请填写 Codex 配置文件 (TOML)!');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const authObj = JSON.parse(authJson);
|
||
// 构造最终对象
|
||
const finalObj = {
|
||
...authObj,
|
||
config: configToml
|
||
};
|
||
configJson = JSON.stringify(finalObj);
|
||
} catch (e) {
|
||
alert('❌ Codex 认证信息不是有效的 JSON 格式:' + e.message);
|
||
return;
|
||
}
|
||
} else {
|
||
// 其他应用使用通用 JSON 输入框
|
||
configJson = document.getElementById('configJson').value.trim();
|
||
if (!configJson) {
|
||
alert('❌ 配置文件模式下,请填写配置文件内容!');
|
||
return;
|
||
}
|
||
// 验证 JSON 格式
|
||
try {
|
||
JSON.parse(configJson);
|
||
} catch (e) {
|
||
alert('❌ 配置文件不是有效的 JSON 格式:' + e.message);
|
||
return;
|
||
}
|
||
}
|
||
|
||
const overrideApiKey = document.getElementById('overrideApiKey').value.trim();
|
||
const overrideEndpoint = document.getElementById('overrideEndpoint').value.trim();
|
||
const model = document.getElementById('model').value.trim();
|
||
const icon = document.getElementById('icon').value.trim();
|
||
const enabled = document.getElementById('enabled').value;
|
||
const notes = document.getElementById('notes').value.trim();
|
||
|
||
// Claude 专用字段
|
||
const haikuModel = document.getElementById('haikuModel').value.trim();
|
||
const sonnetModel = document.getElementById('sonnetModel').value.trim();
|
||
const opusModel = document.getElementById('opusModel').value.trim();
|
||
|
||
// Base64 编码配置文件
|
||
const configB64 = utf8_to_b64(configJson);
|
||
params.append('config', configB64);
|
||
params.append('configFormat', 'json');
|
||
|
||
// 添加覆盖参数
|
||
if (overrideApiKey) params.append('apiKey', overrideApiKey);
|
||
if (overrideEndpoint) {
|
||
try {
|
||
new URL(overrideEndpoint);
|
||
params.append('endpoint', overrideEndpoint);
|
||
} catch (e) {
|
||
alert('❌ 覆盖端点 URL 格式无效!');
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (model) params.append('model', model);
|
||
if (icon) params.append('icon', icon);
|
||
if (enabled) params.append('enabled', enabled);
|
||
if (notes) params.append('notes', notes);
|
||
|
||
// 添加 Claude 专用模型字段
|
||
if (app === 'claude') {
|
||
if (haikuModel) params.append('haikuModel', haikuModel);
|
||
if (sonnetModel) params.append('sonnetModel', sonnetModel);
|
||
if (opusModel) params.append('opusModel', opusModel);
|
||
}
|
||
}
|
||
|
||
const deepLink = `ccswitch://v1/import?${params.toString()}`;
|
||
|
||
// 显示结果
|
||
document.getElementById('linkText').textContent = deepLink;
|
||
document.getElementById('testLink').href = deepLink;
|
||
document.getElementById('result').style.display = 'block';
|
||
|
||
// 滚动到结果区域
|
||
document.getElementById('result').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
}
|
||
|
||
function copyLink() {
|
||
const linkText = document.getElementById('linkText').textContent;
|
||
|
||
navigator.clipboard.writeText(linkText).then(() => {
|
||
const btn = event.target;
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '✅ 已复制!';
|
||
btn.style.background = 'linear-gradient(135deg, #27ae60 0%, #229954 100%)';
|
||
|
||
setTimeout(() => {
|
||
btn.textContent = originalText;
|
||
btn.style.background = '';
|
||
}, 2000);
|
||
}).catch(err => {
|
||
console.error('复制失败:', err);
|
||
alert('❌ 复制失败,请手动复制链接');
|
||
});
|
||
}
|
||
|
||
// 复制深链接
|
||
function copyDeepLink(url, button) {
|
||
navigator.clipboard.writeText(url).then(() => {
|
||
const originalText = button.textContent;
|
||
button.textContent = '✅ 已复制!';
|
||
button.style.background = 'linear-gradient(135deg, #27ae60 0%, #229954 100%)';
|
||
|
||
setTimeout(() => {
|
||
button.textContent = originalText;
|
||
button.style.background = '';
|
||
}, 2000);
|
||
}).catch(err => {
|
||
console.error('复制失败:', err);
|
||
alert('❌ 复制失败,请手动复制链接');
|
||
});
|
||
}
|
||
|
||
// 深链接解析器
|
||
function parseDeepLink() {
|
||
const urlInput = document.getElementById('parseUrl').value.trim();
|
||
|
||
if (!urlInput) {
|
||
alert('❌ 请输入深链接 URL!');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 解析 URL
|
||
const url = new URL(urlInput);
|
||
|
||
// 验证协议
|
||
if (url.protocol !== 'ccswitch:') {
|
||
alert('❌ 无效的深链接协议!必须以 ccswitch:// 开头');
|
||
return;
|
||
}
|
||
|
||
// 提取版本和路径
|
||
const version = url.hostname;
|
||
const path = url.pathname;
|
||
|
||
// 解析查询参数
|
||
const params = new URLSearchParams(url.search);
|
||
const paramsObj = {};
|
||
params.forEach((value, key) => {
|
||
paramsObj[key] = value;
|
||
});
|
||
|
||
// 构建基本信息 HTML
|
||
let basicInfoHtml = `
|
||
<div style="background: #e8f4f8; padding: 16px; border-radius: 8px; border-left: 4px solid #3498db;">
|
||
<h4 style="margin-bottom: 12px; color: #2c3e50;">📋 基本信息</h4>
|
||
<div style="display: grid; grid-template-columns: 120px 1fr; gap: 8px; font-size: 14px;">
|
||
<div style="color: #7f8c8d;">协议版本:</div>
|
||
<div style="font-weight: 600; color: #2c3e50;">${version}</div>
|
||
<div style="color: #7f8c8d;">路径:</div>
|
||
<div style="font-weight: 600; color: #2c3e50;">${path}</div>
|
||
<div style="color: #7f8c8d;">资源类型:</div>
|
||
<div style="font-weight: 600; color: #2c3e50;">${paramsObj.resource || '-'}</div>
|
||
<div style="color: #7f8c8d;">应用类型:</div>
|
||
<div style="font-weight: 600; color: #2c3e50; text-transform: capitalize;">${paramsObj.app || '-'}</div>
|
||
<div style="color: #7f8c8d;">供应商名称:</div>
|
||
<div style="font-weight: 600; color: #2c3e50;">${paramsObj.name || '-'}</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 构建 URL 参数 HTML
|
||
let urlParamsHtml = `
|
||
<div style="background: #fff3cd; padding: 16px; border-radius: 8px; border-left: 4px solid #ffc107;">
|
||
<h4 style="margin-bottom: 12px; color: #856404;">🔗 URL 参数</h4>
|
||
<div style="display: grid; grid-template-columns: 150px 1fr; gap: 8px; font-size: 13px;">
|
||
`;
|
||
|
||
// 常规参数
|
||
const regularParams = ['homepage', 'endpoint', 'apiKey', 'model', 'notes'];
|
||
regularParams.forEach(key => {
|
||
if (paramsObj[key]) {
|
||
let displayValue = paramsObj[key];
|
||
// API Key 掩码处理
|
||
if (key === 'apiKey') {
|
||
displayValue = displayValue.substring(0, 10) + '****';
|
||
}
|
||
urlParamsHtml += `
|
||
<div style="color: #856404; font-weight: 500;">${key}:</div>
|
||
<div style="color: #856404; word-break: break-all; font-family: monospace; font-size: 12px;">${displayValue}</div>
|
||
`;
|
||
}
|
||
});
|
||
|
||
// Claude 专用模型参数
|
||
const claudeModelParams = ['haikuModel', 'sonnetModel', 'opusModel'];
|
||
claudeModelParams.forEach(key => {
|
||
if (paramsObj[key]) {
|
||
urlParamsHtml += `
|
||
<div style="color: #856404; font-weight: 500;">${key}:</div>
|
||
<div style="color: #856404; word-break: break-all; font-family: monospace; font-size: 12px;">${paramsObj[key]}</div>
|
||
`;
|
||
}
|
||
});
|
||
|
||
urlParamsHtml += '</div></div>';
|
||
|
||
// 配置文件解析
|
||
let configHtml = '';
|
||
if (paramsObj.config) {
|
||
try {
|
||
// 解码 Base64
|
||
const decoded = b64_to_utf8(paramsObj.config);
|
||
const configObj = JSON.parse(decoded);
|
||
|
||
configHtml = `
|
||
<div style="background: #d1ecf1; padding: 16px; border-radius: 8px; border-left: 4px solid #17a2b8;">
|
||
<h4 style="margin-bottom: 12px; color: #0c5460;">📄 配置文件内容 (${paramsObj.configFormat?.toUpperCase() || 'JSON'})</h4>
|
||
`;
|
||
|
||
// 根据应用类型解析配置
|
||
if (paramsObj.app === 'claude') {
|
||
const env = configObj.env || {};
|
||
configHtml += `
|
||
<div style="background: #fff; padding: 12px; border-radius: 6px; margin-bottom: 12px;">
|
||
<div style="font-size: 12px; color: #0c5460; margin-bottom: 8px; font-weight: 600;">Claude 环境变量:</div>
|
||
<div style="display: grid; grid-template-columns: 250px 1fr; gap: 6px; font-size: 12px; font-family: monospace;">
|
||
`;
|
||
Object.keys(env).forEach(key => {
|
||
let value = env[key];
|
||
if (key.includes('TOKEN') || key.includes('KEY')) {
|
||
value = value.substring(0, 10) + '****';
|
||
}
|
||
configHtml += `
|
||
<div style="color: #0c5460; font-weight: 500;">${key}:</div>
|
||
<div style="color: #0c5460;">${value}</div>
|
||
`;
|
||
});
|
||
configHtml += '</div></div>';
|
||
} else if (paramsObj.app === 'codex') {
|
||
const auth = configObj.auth || {};
|
||
const config = configObj.config || '';
|
||
|
||
configHtml += `
|
||
<div style="background: #fff; padding: 12px; border-radius: 6px; margin-bottom: 12px;">
|
||
<div style="font-size: 12px; color: #0c5460; margin-bottom: 8px; font-weight: 600;">Codex 认证信息:</div>
|
||
<div style="display: grid; grid-template-columns: 200px 1fr; gap: 6px; font-size: 12px; font-family: monospace;">
|
||
`;
|
||
Object.keys(auth).forEach(key => {
|
||
let value = auth[key];
|
||
if (key.includes('KEY')) {
|
||
value = value.substring(0, 10) + '****';
|
||
}
|
||
configHtml += `
|
||
<div style="color: #0c5460; font-weight: 500;">${key}:</div>
|
||
<div style="color: #0c5460;">${value}</div>
|
||
`;
|
||
});
|
||
configHtml += '</div></div>';
|
||
|
||
if (config) {
|
||
configHtml += `
|
||
<div style="background: #fff; padding: 12px; border-radius: 6px;">
|
||
<div style="font-size: 12px; color: #0c5460; margin-bottom: 8px; font-weight: 600;">TOML 配置:</div>
|
||
<pre style="margin: 0; font-size: 11px; color: #0c5460; white-space: pre-wrap; word-break: break-all;">${config}</pre>
|
||
</div>
|
||
`;
|
||
}
|
||
} else if (paramsObj.app === 'gemini') {
|
||
configHtml += `
|
||
<div style="background: #fff; padding: 12px; border-radius: 6px;">
|
||
<div style="font-size: 12px; color: #0c5460; margin-bottom: 8px; font-weight: 600;">Gemini 环境变量:</div>
|
||
<div style="display: grid; grid-template-columns: 200px 1fr; gap: 6px; font-size: 12px; font-family: monospace;">
|
||
`;
|
||
Object.keys(configObj).forEach(key => {
|
||
let value = configObj[key];
|
||
if (key.includes('KEY')) {
|
||
value = value.substring(0, 10) + '****';
|
||
}
|
||
configHtml += `
|
||
<div style="color: #0c5460; font-weight: 500;">${key}:</div>
|
||
<div style="color: #0c5460;">${value}</div>
|
||
`;
|
||
});
|
||
configHtml += '</div></div>';
|
||
}
|
||
|
||
// 原始 JSON
|
||
configHtml += `
|
||
<details style="margin-top: 12px;">
|
||
<summary style="cursor: pointer; color: #0c5460; font-size: 12px; font-weight: 600;">查看原始 JSON →</summary>
|
||
<pre style="margin-top: 8px; padding: 12px; background: #f8f9fa; border-radius: 6px; font-size: 11px; overflow-x: auto; border: 1px solid #dee2e6;">${JSON.stringify(configObj, null, 2)}</pre>
|
||
</details>
|
||
`;
|
||
|
||
configHtml += '</div>';
|
||
} catch (e) {
|
||
configHtml = `
|
||
<div style="background: #f8d7da; padding: 16px; border-radius: 8px; border-left: 4px solid #dc3545;">
|
||
<h4 style="margin-bottom: 8px; color: #721c24;">❌ 配置文件解析失败</h4>
|
||
<div style="color: #721c24; font-size: 13px;">${e.message}</div>
|
||
</div>
|
||
`;
|
||
}
|
||
}
|
||
|
||
// 显示结果
|
||
document.getElementById('parseBasicInfo').innerHTML = basicInfoHtml;
|
||
document.getElementById('parseUrlParams').innerHTML = urlParamsHtml;
|
||
document.getElementById('parseConfigContent').innerHTML = configHtml;
|
||
document.getElementById('parseResult').style.display = 'block';
|
||
|
||
// 滚动到结果
|
||
document.getElementById('parseResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
|
||
} catch (e) {
|
||
alert('❌ 深链接解析失败:' + e.message);
|
||
console.error('Parse error:', e);
|
||
}
|
||
}
|
||
|
||
// 阻止表单默认提交行为
|
||
document.addEventListener('DOMContentLoaded', function () {
|
||
const inputs = document.querySelectorAll('input, textarea, select');
|
||
inputs.forEach(input => {
|
||
input.addEventListener('keypress', function (e) {
|
||
if (e.key === 'Enter' && e.target.tagName !== 'TEXTAREA') {
|
||
e.preventDefault();
|
||
generateLink();
|
||
}
|
||
});
|
||
});
|
||
|
||
// 初始化显示 Claude 字段
|
||
updateModelFields();
|
||
});
|
||
|
||
// Base64 编码功能
|
||
function encodeToBase64() {
|
||
const input = document.getElementById('encodeInput').value;
|
||
|
||
if (!input.trim()) {
|
||
alert('❌ 请输入要编码的内容!');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const encoded = utf8_to_b64(input);
|
||
document.getElementById('encodeOutput').textContent = encoded;
|
||
document.getElementById('encodeResult').style.display = 'block';
|
||
|
||
// 滚动到结果
|
||
document.getElementById('encodeResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
} catch (e) {
|
||
alert('❌ 编码失败:' + e.message);
|
||
console.error('Encode error:', e);
|
||
}
|
||
}
|
||
|
||
// Base64 解码功能
|
||
function decodeFromBase64() {
|
||
const input = document.getElementById('decodeInput').value.trim();
|
||
|
||
if (!input) {
|
||
alert('❌ 请输入要解码的 Base64 内容!');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const decoded = b64_to_utf8(input);
|
||
document.getElementById('decodeOutput').textContent = decoded;
|
||
document.getElementById('decodeResult').style.display = 'block';
|
||
|
||
// 检查是否是 JSON,如果是则显示格式化按钮
|
||
try {
|
||
JSON.parse(decoded);
|
||
document.getElementById('jsonFormat').style.display = 'block';
|
||
} catch {
|
||
document.getElementById('jsonFormat').style.display = 'none';
|
||
}
|
||
|
||
// 滚动到结果
|
||
document.getElementById('decodeResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
} catch (e) {
|
||
alert('❌ 解码失败:' + e.message + '\n\n请确保输入的是有效的 Base64 编码');
|
||
console.error('Decode error:', e);
|
||
}
|
||
}
|
||
|
||
// 格式化 JSON
|
||
function formatJson() {
|
||
try {
|
||
const text = document.getElementById('decodeOutput').textContent;
|
||
const obj = JSON.parse(text);
|
||
const formatted = JSON.stringify(obj, null, 2);
|
||
document.getElementById('decodeOutput').textContent = formatted;
|
||
} catch (e) {
|
||
alert('❌ JSON 格式化失败:' + e.message);
|
||
}
|
||
}
|
||
|
||
// 复制编码结果
|
||
function copyEncoded() {
|
||
const text = document.getElementById('encodeOutput').textContent;
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
const btn = event.target;
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '✅ 已复制!';
|
||
btn.style.background = 'linear-gradient(135deg, #27ae60 0%, #229954 100%)';
|
||
|
||
setTimeout(() => {
|
||
btn.textContent = originalText;
|
||
btn.style.background = '';
|
||
}, 2000);
|
||
}).catch(err => {
|
||
console.error('复制失败:', err);
|
||
alert('❌ 复制失败,请手动复制');
|
||
});
|
||
}
|
||
|
||
// 复制解码结果
|
||
function copyDecoded() {
|
||
const text = document.getElementById('decodeOutput').textContent;
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
const btn = event.target;
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '✅ 已复制!';
|
||
btn.style.background = 'linear-gradient(135deg, #27ae60 0%, #229954 100%)';
|
||
|
||
setTimeout(() => {
|
||
btn.textContent = originalText;
|
||
btn.style.background = '';
|
||
}, 2000);
|
||
}).catch(err => {
|
||
console.error('复制失败:', err);
|
||
alert('❌ 复制失败,请手动复制');
|
||
});
|
||
}
|
||
|
||
// ==================== 深链接生成器函数 ====================
|
||
|
||
// 生成供应商深链接
|
||
function generateProviderLink() {
|
||
const app = document.getElementById('providerApp').value;
|
||
const name = document.getElementById('providerName').value.trim();
|
||
const apiKey = document.getElementById('providerApiKey').value.trim();
|
||
const endpoint = document.getElementById('providerEndpoint').value.trim();
|
||
const homepage = document.getElementById('providerHomepage').value.trim();
|
||
const model = document.getElementById('providerModel').value.trim();
|
||
const notes = document.getElementById('providerNotes').value.trim();
|
||
const enabled = document.getElementById('providerEnabled').value;
|
||
|
||
// 验证必填字段
|
||
if (!name) {
|
||
alert('❌ 请填写供应商名称');
|
||
return;
|
||
}
|
||
|
||
if (!apiKey) {
|
||
alert('❌ 请填写 API Key');
|
||
return;
|
||
}
|
||
|
||
if (!endpoint) {
|
||
alert('❌ 请填写 API Endpoint');
|
||
return;
|
||
}
|
||
|
||
if (!homepage) {
|
||
alert('❌ 请填写主页链接');
|
||
return;
|
||
}
|
||
|
||
// 构建深链接
|
||
let url = `ccswitch://v1/import?resource=provider&app=${app}&name=${encodeURIComponent(name)}&endpoint=${encodeURIComponent(endpoint)}&homepage=${encodeURIComponent(homepage)}&apiKey=${encodeURIComponent(apiKey)}`;
|
||
|
||
if (model) {
|
||
url += `&model=${encodeURIComponent(model)}`;
|
||
}
|
||
|
||
if (notes) {
|
||
url += `¬es=${encodeURIComponent(notes)}`;
|
||
}
|
||
|
||
if (enabled === 'true') {
|
||
url += '&enabled=true';
|
||
}
|
||
|
||
// 显示结果
|
||
document.getElementById('providerUrl').textContent = url;
|
||
document.getElementById('providerImportBtn').href = url;
|
||
document.getElementById('providerResult').style.display = 'block';
|
||
|
||
// 滚动到结果
|
||
document.getElementById('providerResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
}
|
||
|
||
// 生成 MCP 深链接
|
||
function generateMcpLink() {
|
||
const apps = document.getElementById('mcpApps').value.trim();
|
||
const config = document.getElementById('mcpConfig').value.trim();
|
||
const enabled = document.getElementById('mcpEnabled').value;
|
||
|
||
if (!apps) {
|
||
alert('❌ 请填写目标应用');
|
||
return;
|
||
}
|
||
|
||
if (!config) {
|
||
alert('❌ 请填写 MCP 配置');
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 验证 JSON 格式
|
||
const jsonObj = JSON.parse(config);
|
||
if (!jsonObj.mcpServers) {
|
||
alert('❌ 配置必须包含 mcpServers 字段');
|
||
return;
|
||
}
|
||
|
||
// Base64 编码配置
|
||
const configB64 = utf8_to_b64(config);
|
||
|
||
// 构建深链接
|
||
let url = `ccswitch://v1/import?resource=mcp&apps=${encodeURIComponent(apps)}&config=${encodeURIComponent(configB64)}`;
|
||
|
||
if (enabled === 'true') {
|
||
url += '&enabled=true';
|
||
}
|
||
|
||
// 显示结果
|
||
document.getElementById('mcpUrl').textContent = url;
|
||
document.getElementById('mcpImportBtn').href = url;
|
||
document.getElementById('mcpResult').style.display = 'block';
|
||
|
||
// 滚动到结果
|
||
document.getElementById('mcpResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
} catch (e) {
|
||
alert('❌ JSON 格式错误:' + e.message);
|
||
}
|
||
}
|
||
|
||
// 生成 Prompt 深链接
|
||
function generatePromptLink() {
|
||
const app = document.getElementById('promptApp').value;
|
||
const name = document.getElementById('promptName').value.trim();
|
||
const content = document.getElementById('promptContent').value.trim();
|
||
const description = document.getElementById('promptDescription').value.trim();
|
||
const enabled = document.getElementById('promptEnabled').value;
|
||
|
||
if (!name) {
|
||
alert('❌ 请填写提示词名称');
|
||
return;
|
||
}
|
||
|
||
if (!content) {
|
||
alert('❌ 请填写提示词内容');
|
||
return;
|
||
}
|
||
|
||
// Base64 编码内容
|
||
const contentB64 = utf8_to_b64(content);
|
||
|
||
// 构建深链接
|
||
let url = `ccswitch://v1/import?resource=prompt&app=${app}&name=${encodeURIComponent(name)}&content=${encodeURIComponent(contentB64)}`;
|
||
|
||
if (description) {
|
||
url += `&description=${encodeURIComponent(description)}`;
|
||
}
|
||
|
||
if (enabled === 'true') {
|
||
url += '&enabled=true';
|
||
}
|
||
|
||
// 显示结果
|
||
document.getElementById('promptUrl').textContent = url;
|
||
document.getElementById('promptImportBtn').href = url;
|
||
document.getElementById('promptResult').style.display = 'block';
|
||
|
||
// 滚动到结果
|
||
document.getElementById('promptResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
}
|
||
|
||
// 生成 Skill 深链接
|
||
function generateSkillLink() {
|
||
const repo = document.getElementById('skillRepo').value.trim();
|
||
const branch = document.getElementById('skillBranch').value.trim() || 'main';
|
||
const skillsPath = document.getElementById('skillPath').value.trim() || 'skills';
|
||
const directory = document.getElementById('skillDirectory').value.trim();
|
||
|
||
if (!repo) {
|
||
alert('❌ 请填写 GitHub 仓库');
|
||
return;
|
||
}
|
||
|
||
// 验证仓库格式
|
||
if (!repo.includes('/')) {
|
||
alert('❌ 仓库格式应为: owner/repo-name');
|
||
return;
|
||
}
|
||
|
||
// 构建深链接
|
||
let url = `ccswitch://v1/import?resource=skill&repo=${encodeURIComponent(repo)}&branch=${encodeURIComponent(branch)}&skills_path=${encodeURIComponent(skillsPath)}`;
|
||
|
||
if (directory) {
|
||
url += `&directory=${encodeURIComponent(directory)}`;
|
||
}
|
||
|
||
// 显示结果
|
||
document.getElementById('skillUrl').textContent = url;
|
||
document.getElementById('skillImportBtn').href = url;
|
||
document.getElementById('skillResult').style.display = 'block';
|
||
|
||
// 滚动到结果
|
||
document.getElementById('skillResult').scrollIntoView({
|
||
behavior: 'smooth',
|
||
block: 'nearest'
|
||
});
|
||
}
|
||
|
||
// 复制生成的链接
|
||
function copyGeneratedLink(elementId) {
|
||
const text = document.getElementById(elementId).textContent;
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
const btn = event.target;
|
||
const originalText = btn.textContent;
|
||
btn.textContent = '✅ 已复制!';
|
||
btn.style.background = 'linear-gradient(135deg, #27ae60 0%, #229954 100%)';
|
||
|
||
setTimeout(() => {
|
||
btn.textContent = originalText;
|
||
btn.style.background = '';
|
||
}, 2000);
|
||
}).catch(err => {
|
||
console.error('复制失败:', err);
|
||
alert('❌ 复制失败,请手动复制');
|
||
});
|
||
}
|
||
|
||
// 选中文本(点击 URL 时)
|
||
function selectText(element) {
|
||
const range = document.createRange();
|
||
range.selectNodeContents(element);
|
||
const selection = window.getSelection();
|
||
selection.removeAllRanges();
|
||
selection.addRange(range);
|
||
}
|
||
</script>
|
||
</body>
|
||
|
||
</html> |