Files
CC-Switch/src/utils/formatters.ts
T
YoVinchen 3d69da5b66 feat: add model configuration support and fix Gemini deeplink bug (#251)
* feat(providers): add notes field for provider management

- Add notes field to Provider model (backend and frontend)
- Display notes with higher priority than URL in provider card
- Style notes as non-clickable text to differentiate from URLs
- Add notes input field in provider form
- Add i18n support (zh/en) for notes field

* chore: format code and clean up unused props

- Run cargo fmt on Rust backend code
- Format TypeScript imports and code style
- Remove unused appId prop from ProviderPresetSelector
- Clean up unused variables in tests
- Integrate notes field handling in provider dialogs

* feat(deeplink): implement ccswitch:// protocol for provider import

Add deep link support to enable one-click provider configuration import via ccswitch:// URLs.

Backend:
- Implement URL parsing and validation (src-tauri/src/deeplink.rs)
- Add Tauri commands for parse and import (src-tauri/src/commands/deeplink.rs)
- Register ccswitch:// protocol in macOS Info.plist
- Add comprehensive unit tests (src-tauri/tests/deeplink_import.rs)

Frontend:
- Create confirmation dialog with security review UI (src/components/DeepLinkImportDialog.tsx)
- Add API wrapper (src/lib/api/deeplink.ts)
- Integrate event listeners in App.tsx

Configuration:
- Update Tauri config for deep link handling
- Add i18n support for Chinese and English
- Include test page for deep link validation (deeplink-test.html)

Files: 15 changed, 1312 insertions(+)

* chore(deeplink): integrate deep link handling into app lifecycle

Wire up deep link infrastructure with app initialization and event handling.

Backend Integration:
- Register deep link module and commands in mod.rs
- Add URL handling in app setup (src-tauri/src/lib.rs:handle_deeplink_url)
- Handle deep links from single instance callback (Windows/Linux CLI)
- Handle deep links from macOS system events
- Add tauri-plugin-deep-link dependency (Cargo.toml)

Frontend Integration:
- Listen for deeplink-import/deeplink-error events in App.tsx
- Update DeepLinkImportDialog component imports

Configuration:
- Enable deep link plugin in tauri.conf.json
- Update Cargo.lock for new dependencies

Localization:
- Add Chinese translations for deep link UI (zh.json)
- Add English translations for deep link UI (en.json)

Files: 9 changed, 359 insertions(+), 18 deletions(-)

* refactor(deeplink): enhance Codex provider template generation

Align deep link import with UI preset generation logic by:
- Adding complete config.toml template matching frontend defaults
- Generating safe provider name from sanitized input
- Including model_provider, reasoning_effort, and wire_api settings
- Removing minimal template that only contained base_url
- Cleaning up deprecated test file deeplink-test.html

* style: fix clippy uninlined_format_args warnings

Apply clippy --fix to use inline format arguments in:
- src/mcp.rs (8 fixes)
- src/services/env_manager.rs (10 fixes)

* style: apply code formatting and cleanup

- Format TypeScript files with Prettier (App.tsx, EnvWarningBanner.tsx, formatters.ts)
- Organize Rust imports and module order alphabetically
- Add newline at end of JSON files (en.json, zh.json)
- Update Cargo.lock for dependency changes

* feat: add model name configuration support for Codex and fix Gemini model handling

- Add visual model name input field for Codex providers
  - Add model name extraction and update utilities in providerConfigUtils
  - Implement model name state management in useCodexConfigState hook
  - Add conditional model field rendering in CodexFormFields (non-official only)
  - Integrate model name sync with TOML config in ProviderForm

- Fix Gemini deeplink model injection bug
  - Correct environment variable name from GOOGLE_GEMINI_MODEL to GEMINI_MODEL
  - Add test cases for Gemini model injection (with/without model)
  - All tests passing (9/9)

- Fix Gemini model field binding in edit mode
  - Add geminiModel state to useGeminiConfigState hook
  - Extract model value during initialization and reset
  - Sync model field with geminiEnv state to prevent data loss on submit
  - Fix missing model value display when editing Gemini providers

Changes:
  - 6 files changed, 245 insertions(+), 13 deletions(-)
2025-11-19 09:03:18 +08:00

81 lines
2.3 KiB
TypeScript

/**
* 格式化 JSON 字符串
* @param value - 原始 JSON 字符串(支持带键名包装的格式)
* @returns 格式化后的 JSON 字符串(2 空格缩进)
* @throws 如果 JSON 格式无效
*/
export function formatJSON(value: string): string {
const trimmed = value.trim();
if (!trimmed) {
return "";
}
// 使用智能解析器来处理可能的片段格式
const result = parseSmartMcpJson(trimmed);
return result.formattedConfig;
}
/**
* 智能解析 MCP JSON 配置
* 支持两种格式:
* 1. 纯配置对象:{ "command": "npx", "args": [...], ... }
* 2. 带键名包装: "server-name": { "command": "npx", ... } 或 { "server-name": {...} }
*
* @param jsonText - JSON 字符串
* @returns { id?: string, config: object, formattedConfig: string }
* @throws 如果 JSON 格式无效
*/
export function parseSmartMcpJson(jsonText: string): {
id?: string;
config: any;
formattedConfig: string;
} {
let trimmed = jsonText.trim();
if (!trimmed) {
return { config: {}, formattedConfig: "" };
}
// 如果是键值对片段("key": {...}),包装成完整对象
if (trimmed.startsWith('"') && !trimmed.startsWith("{")) {
trimmed = `{${trimmed}}`;
}
const parsed = JSON.parse(trimmed);
// 如果是单键对象且值是对象,提取键名和配置
const keys = Object.keys(parsed);
if (
keys.length === 1 &&
parsed[keys[0]] &&
typeof parsed[keys[0]] === "object" &&
!Array.isArray(parsed[keys[0]])
) {
const id = keys[0];
const config = parsed[id];
return {
id,
config,
formattedConfig: JSON.stringify(config, null, 2),
};
}
// 否则直接使用
return {
config: parsed,
formattedConfig: JSON.stringify(parsed, null, 2),
};
}
/**
* TOML 格式化功能已禁用
*
* 原因:smol-toml 的 parse/stringify 会丢失所有注释和原有排版。
* 由于 TOML 常用于配置文件,注释是重要的文档说明,丢失注释会造成严重的用户体验问题。
*
* 未来可选方案:
* - 使用 @ltd/j-toml(支持注释保留,但需额外依赖和复杂的 API)
* - 实现仅格式化缩进/空白的轻量级方案
* - 使用 toml-eslint-parser + 自定义生成器
*
* 暂时建议:依赖现有的 TOML 语法校验(useCodexTomlValidation),不提供格式化功能。
*/