docs: translate Chinese comments and JSDoc to English across 14 files

Standardize code documentation language to English for better international
collaboration, covering ai-bridge services, Java backend, webview scripts,
and frontend utilities.
This commit is contained in:
zkpaiminmin
2026-03-17 19:16:38 +08:00
parent 6d2480f13a
commit 96ec9b89cc
14 changed files with 133 additions and 132 deletions
+22 -22
View File
@@ -1,6 +1,6 @@
/**
* 收藏服务模块
* 负责管理会话收藏功能
* Favorites service module
* Responsible for managing session favorites
*/
const fs = require('fs');
@@ -11,7 +11,7 @@ const FAVORITES_DIR = getCodemossDir();
const FAVORITES_FILE = path.join(FAVORITES_DIR, 'favorites.json');
/**
* 确保收藏目录存在
* Ensure the favorites directory exists
*/
function ensureFavoritesDir() {
if (!fs.existsSync(FAVORITES_DIR)) {
@@ -20,8 +20,8 @@ function ensureFavoritesDir() {
}
/**
* 加载收藏数据
* @returns {Object} 收藏数据,格式: { "sessionId": { "favoritedAt": timestamp } }
* Load favorites data
* @returns {Object} Favorites data in the format: { "sessionId": { "favoritedAt": timestamp } }
*/
function loadFavorites() {
try {
@@ -40,8 +40,8 @@ function loadFavorites() {
}
/**
* 保存收藏数据
* @param {Object} favorites - 收藏数据
* Save favorites data
* @param {Object} favorites - Favorites data
*/
function saveFavorites(favorites) {
try {
@@ -54,9 +54,9 @@ function saveFavorites(favorites) {
}
/**
* 添加收藏
* @param {string} sessionId - 会话ID
* @returns {boolean} 是否成功
* Add a favorite
* @param {string} sessionId - Session ID
* @returns {boolean} Whether the operation succeeded
*/
function addFavorite(sessionId) {
try {
@@ -81,9 +81,9 @@ function addFavorite(sessionId) {
}
/**
* 移除收藏
* @param {string} sessionId - 会话ID
* @returns {boolean} 是否成功
* Remove a favorite
* @param {string} sessionId - Session ID
* @returns {boolean} Whether the operation succeeded
*/
function removeFavorite(sessionId) {
try {
@@ -106,8 +106,8 @@ function removeFavorite(sessionId) {
}
/**
* 切换收藏状态
* @param {string} sessionId - 会话ID
* Toggle favorite status
* @param {string} sessionId - Session ID
* @returns {Object} { success: boolean, isFavorited: boolean }
*/
function toggleFavorite(sessionId) {
@@ -136,8 +136,8 @@ function toggleFavorite(sessionId) {
}
/**
* 检查会话是否已收藏
* @param {string} sessionId - 会话ID
* Check whether a session is favorited
* @param {string} sessionId - Session ID
* @returns {boolean}
*/
function isFavorited(sessionId) {
@@ -146,9 +146,9 @@ function isFavorited(sessionId) {
}
/**
* 获取收藏时间
* @param {string} sessionId - 会话ID
* @returns {number|null} 收藏时间戳,未收藏返回 null
* Get the favorite timestamp
* @param {string} sessionId - Session ID
* @returns {number|null} Favorite timestamp, or null if not favorited
*/
function getFavoritedAt(sessionId) {
const favorites = loadFavorites();
@@ -156,7 +156,7 @@ function getFavoritedAt(sessionId) {
}
/**
* 获取所有收藏的会话ID列表(按收藏时间倒序)
* Get all favorited session IDs in reverse favorite-time order
* @returns {string[]}
*/
function getFavoritedSessionIds() {
@@ -167,7 +167,7 @@ function getFavoritedSessionIds() {
.map(([sessionId]) => sessionId);
}
// 使用 CommonJS 导出
// Export via CommonJS
module.exports = {
loadFavorites,
addFavorite,
+26 -26
View File
@@ -1,7 +1,7 @@
/**
* 输入历史记录服务模块
* 负责管理用户输入历史记录的持久化存储
* 存储位置: ~/.codemoss/inputHistory.json
* Input history service module
* Responsible for persistent storage of user input history
* Storage location: ~/.codemoss/inputHistory.json
*/
const fs = require('fs');
@@ -11,14 +11,14 @@ const { getCodemossDir } = require('../utils/path-utils.cjs');
const CODEMOSS_DIR = getCodemossDir();
const HISTORY_FILE = path.join(CODEMOSS_DIR, 'inputHistory.json');
/** 最大历史记录条数 */
/** Maximum number of history items */
const MAX_HISTORY_ITEMS = 200;
/** 最大计数记录条数 */
/** Maximum number of count records */
const MAX_COUNT_RECORDS = 200;
/**
* 确保目录存在
* Ensure the directory exists
*/
function ensureDir() {
if (!fs.existsSync(CODEMOSS_DIR)) {
@@ -27,7 +27,7 @@ function ensureDir() {
}
/**
* 读取历史数据文件
* Read the history data file
* @returns {{ items: string[], counts: Record<string, number> }}
*/
function readHistoryFile() {
@@ -52,7 +52,7 @@ function readHistoryFile() {
}
/**
* 写入历史数据文件
* Write the history data file
* @param {{ items: string[], counts: Record<string, number> }} data
*/
function writeHistoryFile(data) {
@@ -66,7 +66,7 @@ function writeHistoryFile(data) {
}
/**
* 加载历史记录列表
* Load the history item list
* @returns {string[]}
*/
function loadHistory() {
@@ -75,7 +75,7 @@ function loadHistory() {
}
/**
* 加载使用计数
* Load usage counts
* @returns {Record<string, number>}
*/
function loadCounts() {
@@ -84,7 +84,7 @@ function loadCounts() {
}
/**
* 清理计数记录,保留使用频率最高的
* Trim count records and keep the most frequently used entries
* @param {Record<string, number>} counts
* @returns {Record<string, number>}
*/
@@ -92,15 +92,15 @@ function cleanupCounts(counts) {
const entries = Object.entries(counts);
if (entries.length <= MAX_COUNT_RECORDS) return counts;
// 按计数降序排序,保留前 MAX_COUNT_RECORDS
// Sort by count in descending order and keep the top MAX_COUNT_RECORDS entries
entries.sort((a, b) => b[1] - a[1]);
const kept = entries.slice(0, MAX_COUNT_RECORDS);
return Object.fromEntries(kept);
}
/**
* 记录历史(包括拆分片段)
* @param {string[]} fragments - 要记录的片段数组
* Record history, including split fragments
* @param {string[]} fragments - Array of fragments to record
* @returns {{ success: boolean, items: string[] }}
*/
function recordHistory(fragments) {
@@ -112,21 +112,21 @@ function recordHistory(fragments) {
const data = readHistoryFile();
let { items, counts } = data;
// 增加每个片段的使用计数
// Increment the usage count for each fragment
for (const fragment of fragments) {
counts[fragment] = (counts[fragment] || 0) + 1;
}
// 清理计数
// Trim count records
counts = cleanupCounts(counts);
// 创建新片段集合用于快速查找
// Create a set of incoming fragments for fast lookups
const newFragmentsSet = new Set(fragments);
// 移除已存在的片段以避免重复
// Remove existing fragments to avoid duplicates
const filteredItems = items.filter(item => !newFragmentsSet.has(item));
// 添加新片段到末尾
// Append new fragments to the end
const newItems = [...filteredItems, ...fragments].slice(-MAX_HISTORY_ITEMS);
writeHistoryFile({ items: newItems, counts });
@@ -139,8 +139,8 @@ function recordHistory(fragments) {
}
/**
* 删除单条历史记录
* @param {string} item - 要删除的记录
* Delete a single history entry
* @param {string} item - Entry to delete
* @returns {{ success: boolean, items: string[] }}
*/
function deleteHistoryItem(item) {
@@ -148,10 +148,10 @@ function deleteHistoryItem(item) {
const data = readHistoryFile();
let { items, counts } = data;
// 从列表中移除
// Remove it from the item list
items = items.filter(i => i !== item);
// 从计数中移除
// Remove it from the count map
delete counts[item];
writeHistoryFile({ items, counts });
@@ -164,7 +164,7 @@ function deleteHistoryItem(item) {
}
/**
* 清空所有历史记录
* Clear all history entries
* @returns {{ success: boolean }}
*/
function clearAllHistory() {
@@ -178,14 +178,14 @@ function clearAllHistory() {
}
/**
* 获取所有历史数据(用于设置页面展示)
* Get all history data for the settings page
* @returns {{ items: string[], counts: Record<string, number> }}
*/
function getAllHistoryData() {
return readHistoryFile();
}
// 使用 CommonJS 导出
// Export via CommonJS
module.exports = {
loadHistory,
loadCounts,
+22 -21
View File
@@ -1,6 +1,6 @@
/**
* 会话标题服务模块
* 负责管理会话自定义标题功能
* Session title service module
* Responsible for managing custom session titles
*/
const fs = require('fs');
@@ -11,7 +11,7 @@ const TITLES_DIR = getCodemossDir();
const TITLES_FILE = path.join(TITLES_DIR, 'session-titles.json');
/**
* 确保标题目录存在
* Ensure the title directory exists
*/
function ensureTitlesDir() {
if (!fs.existsSync(TITLES_DIR)) {
@@ -20,8 +20,8 @@ function ensureTitlesDir() {
}
/**
* 加载标题数据
* @returns {Object} 标题数据,格式: { "sessionId": { "customTitle": "标题", "updatedAt": timestamp } }
* Load title data
* @returns {Object} Title data in the format: { "sessionId": { "customTitle": "Title", "updatedAt": timestamp } }
*/
function loadTitles() {
try {
@@ -40,8 +40,9 @@ function loadTitles() {
}
/**
* 保存标题数据(原子写入:先写临时文件再 rename,防止写入中途崩溃导致数据丢失)
* @param {Object} titles - 标题数据
* Save title data using an atomic write:
* write to a temporary file first, then rename it to avoid data loss if a write crashes midway.
* @param {Object} titles - Title data
*/
function saveTitles(titles) {
try {
@@ -56,16 +57,16 @@ function saveTitles(titles) {
}
/**
* 更新会话标题
* @param {string} sessionId - 会话ID
* @param {string} customTitle - 自定义标题
* Update a session title
* @param {string} sessionId - Session ID
* @param {string} customTitle - Custom title
* @returns {Object} { success: boolean, title: string }
*/
function updateTitle(sessionId, customTitle) {
try {
const titles = loadTitles();
// 验证标题长度(最多50个字符)
// Validate title length (maximum 50 characters)
if (customTitle && customTitle.length > 50) {
return {
success: false,
@@ -94,9 +95,9 @@ function updateTitle(sessionId, customTitle) {
}
/**
* 获取会话标题
* @param {string} sessionId - 会话ID
* @returns {string|null} 自定义标题,未设置返回 null
* Get the session title
* @param {string} sessionId - Session ID
* @returns {string|null} Custom title, or null if unset
*/
function getTitle(sessionId) {
const titles = loadTitles();
@@ -104,9 +105,9 @@ function getTitle(sessionId) {
}
/**
* 删除会话标题
* @param {string} sessionId - 会话ID
* @returns {boolean} 是否成功
* Delete a session title
* @param {string} sessionId - Session ID
* @returns {boolean} Whether the operation succeeded
*/
function deleteTitle(sessionId) {
try {
@@ -129,16 +130,16 @@ function deleteTitle(sessionId) {
}
/**
* 获取更新时间
* @param {string} sessionId - 会话ID
* @returns {number|null} 更新时间戳,未设置返回 null
* Get the last updated timestamp
* @param {string} sessionId - Session ID
* @returns {number|null} Updated timestamp, or null if unset
*/
function getUpdatedAt(sessionId) {
const titles = loadTitles();
return titles[sessionId]?.updatedAt || null;
}
// 使用 CommonJS 导出
// Export via CommonJS
module.exports = {
loadTitles,
updateTitle,
+10 -10
View File
@@ -1,19 +1,19 @@
/**
* 路径处理工具模块 (CommonJS 版本)
* 负责路径规范化、用户目录处理
* Path utility module (CommonJS version)
* Responsible for path normalization and home directory handling
*/
const fs = require('fs');
const path = require('path');
const os = require('os');
// 缓存真实的用户目录路径,避免重复计算
// Cache the resolved home directory path to avoid repeated lookups
let cachedRealHomeDir = null;
/**
* 获取真实的用户目录路径.
* 解决 Windows 上用户目录被移动或使用符号链接/Junction 的问题。
* @returns {string} 真实的用户目录路径
* Get the resolved home directory path.
* Handles cases on Windows where the user directory is moved or uses a symlink/junction.
* @returns {string} Resolved home directory path
*/
function getRealHomeDir() {
if (cachedRealHomeDir) {
@@ -32,16 +32,16 @@ function getRealHomeDir() {
}
/**
* 获取 .codemoss 配置目录路径.
* @returns {string} ~/.codemoss 目录路径
* Get the .codemoss configuration directory path.
* @returns {string} ~/.codemoss directory path
*/
function getCodemossDir() {
return path.join(getRealHomeDir(), '.codemoss');
}
/**
* 获取 .claude 配置目录路径.
* @returns {string} ~/.claude 目录路径
* Get the .claude configuration directory path.
* @returns {string} ~/.claude directory path
*/
function getClaudeDir() {
return path.join(getRealHomeDir(), '.claude');
@@ -26,7 +26,7 @@ public class ClaudeNotifier {
public static void showSuccess(@NotNull Project project, String message) {
show(project, "Claude ✓", message, 5000);
// 播放任务完成提示音
// Play the task completion notification sound
SoundNotificationService.getInstance().playTaskCompleteSound();
}
@@ -666,12 +666,12 @@ public class ProviderManager {
}
/**
* 归一化 Claude 当前 Provider
* 首次启动或旧配置未设置 current 时,默认回退到本地 settings.json
* 这样前端初始化时就能拿到稳定的模型映射来源。
* Normalize the current Claude provider.
* On first launch, or when older configs do not define current, default back to the local
* settings.json so the frontend initializes with a stable source for model mapping.
*
* @param config 当前插件配置
* @return 可用的 current provider id
* @param config current plugin configuration
* @return available current provider id
*/
private String normalizeCurrentClaudeProviderId(JsonObject config) {
boolean changed = false;
@@ -696,7 +696,7 @@ public class ProviderManager {
currentId = claude.get("current").getAsString();
}
// current 为空,或指向了已删除的 provider 时,统一回退到本地 settings.json
// If current is blank or points to a deleted provider, fall back to the local settings.json provider.
if (currentId == null
|| currentId.trim().isEmpty()
|| (!LOCAL_SETTINGS_PROVIDER_ID.equals(currentId) && !providers.has(currentId))) {
@@ -24,8 +24,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 声音通知服务
* 负责在任务完成时播放提示音
* Sound notification service
* Responsible for playing notification sounds when a task completes
*/
public class SoundNotificationService {
@@ -47,7 +47,7 @@ public class SoundNotificationService {
SOUND_RESOURCES = Collections.unmodifiableMap(map);
}
// 单例模式
// Singleton pattern
private static volatile SoundNotificationService instance;
private SoundNotificationService() {
@@ -130,7 +130,7 @@ public class SoundNotificationService {
}
/**
* 从资源文件播放音频
* Play audio from a bundled resource.
*/
private void playFromResource(String resourcePath) throws Exception {
try (InputStream rawStream = getClass().getResourceAsStream(resourcePath)) {
@@ -139,7 +139,7 @@ public class SoundNotificationService {
return;
}
// 使用 BufferedInputStream 包装,因为 AudioSystem 需要 mark/reset 支持
// Wrap with BufferedInputStream because AudioSystem requires mark/reset support
try (BufferedInputStream bufferedStream = new BufferedInputStream(rawStream);
AudioInputStream audioIn = AudioSystem.getAudioInputStream(bufferedStream)) {
playAudioStream(audioIn);
@@ -177,7 +177,7 @@ public class SoundNotificationService {
}
/**
* 从文件播放音频
* Play audio from a file.
*/
private void playFromFile(String rawPath) throws Exception {
String normalizedPath = normalizeSoundPath(rawPath);
@@ -194,13 +194,13 @@ public class SoundNotificationService {
String fileName = file.getName().toLowerCase(Locale.ROOT);
// MP3 格式使用 JLayer 在后台线程解码播放
// Decode and play MP3 files with JLayer on a background thread
if (fileName.endsWith(".mp3")) {
playMp3(file);
return;
}
// WAV AIFF 格式使用 Java 标准库播放
// Play WAV and AIFF files with the Java standard library
try (AudioInputStream audioIn = AudioSystem.getAudioInputStream(file)) {
playAudioStream(audioIn);
}
@@ -213,7 +213,7 @@ public class SoundNotificationService {
private static final int MP3_PLAYBACK_TIMEOUT_SECONDS = 30;
/**
* 使用 JLayer 播放 MP3(后台线程调用,阻塞直到播放结束或超时)。
* Play MP3 with JLayer (called from a background thread and blocks until playback completes or times out).
*/
private void playMp3(File file) throws Exception {
try (BufferedInputStream bufferedStream = new BufferedInputStream(new FileInputStream(file))) {
@@ -265,15 +265,15 @@ public class SoundNotificationService {
}
/**
* 验证音频文件是否可用
* Validate whether an audio file is usable.
*
* @param filePath 文件路径
* @return 验证结果,包含成功状态和错误信息
* @param filePath file path
* @return validation result, including success state and error information
*/
public ValidationResult validateSoundFile(String filePath) {
String normalizedPath = normalizeSoundPath(filePath);
if (normalizedPath == null || normalizedPath.isEmpty()) {
return new ValidationResult(true, null); // 空路径表示使用默认声音
return new ValidationResult(true, null); // An empty path means the default sound should be used
}
File file = new File(normalizedPath);
@@ -291,7 +291,7 @@ public class SoundNotificationService {
return new ValidationResult(false, "Only WAV, MP3, AIFF formats are supported");
}
// MP3 使用 JLayer 播放,跳过 AudioSystem 格式校验
// MP3 playback uses JLayer, so skip AudioSystem format validation
if (lowerPath.endsWith(".mp3")) {
return new ValidationResult(true, null);
}
@@ -307,7 +307,7 @@ public class SoundNotificationService {
}
/**
* 验证结果
* Validation result
*/
public record ValidationResult(boolean valid, String errorMessage) {
}
@@ -13,11 +13,11 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
* ProviderManager 回归测试。
* Regression tests for ProviderManager.
*/
public class ProviderManagerTest {
/**
* current 为空时,应默认启用本地 settings.json Provider
* When current is blank, the local settings.json provider should be enabled by default.
*/
@Test
public void shouldDefaultActiveProviderToLocalSettingsWhenCurrentIsBlank() {
@@ -35,7 +35,7 @@ public class ProviderManagerTest {
}
/**
* current 缺失时,Provider 列表里也应把本地 settings.json 标记为启用状态。
* When current is missing, the provider list should also mark the local settings.json provider as active.
*/
@Test
public void shouldMarkLocalProviderAsActiveWhenCurrentIsMissing() {
@@ -58,7 +58,7 @@ public class ProviderManagerTest {
}
/**
* 构造仅使用内存配置的 ProviderManager,避免测试依赖真实文件系统。
* Build a ProviderManager backed only by in-memory config to avoid depending on the real filesystem in tests.
*/
private ProviderManager createProviderManager(AtomicReference<JsonObject> configRef) {
Gson gson = new Gson();
@@ -81,7 +81,7 @@ public class ProviderManagerTest {
}
/**
* 构造最小 Claude 配置。
* Build the minimal Claude configuration.
*/
private JsonObject createConfigWithCurrent(String current) {
JsonObject config = new JsonObject();
+4 -4
View File
@@ -92,10 +92,10 @@ function getProcesses() {
if (!isClaude && !isDaemon && !isStreamJson) continue
// 排除 MCP 插件进程
// Exclude MCP plugin processes
if (isMcp) continue
// 排除系统 daemoncloudd, cfprefsd 等)
// Exclude system daemons (cloudd, cfprefsd, etc.)
if (command.includes('cloudd') || command.includes('cfprefsd')) continue
let type = '未知'
@@ -193,7 +193,7 @@ function ask(question) {
return new Promise(resolve => rl.question(question, answer => { rl.close(); resolve(answer.trim()) }))
}
// --- 主流程 ---
// --- Main flow ---
console.log('\n Claude 进程管理工具')
console.log(' ==================\n')
@@ -217,7 +217,7 @@ if (answer.toLowerCase() === 'y') {
}
}
// 关闭后再查一次
// Check again after closing processes
const remaining = getProcesses()
if (remaining.length > 0) {
console.log(' 关闭后剩余进程:')
+6 -6
View File
@@ -20,9 +20,9 @@ const content = fs.readFileSync(changelogPath, 'utf8');
/**
* Parse CHANGELOG.md into structured entries.
* Handles three format eras:
* - Newer (v0.1.7+): Bilingual with English:/中文: markers and emoji section headers
* - Mid (v0.1.4-v0.1.6): Bilingual with English:/中文: markers and checkbox items
* - Older (< v0.1.4): Chinese only with checkbox items or plain text
* - Newer (v0.1.7+): Bilingual with English and Chinese markers plus emoji section headers
* - Mid (v0.1.4-v0.1.6): Bilingual with English and Chinese markers plus checkbox items
* - Older (< v0.1.4): Chinese-only entries with checkbox items or plain text
*/
function parseChangelog(raw) {
const entries = [];
@@ -45,7 +45,7 @@ function parseChangelog(raw) {
const nextIndex = i + 1 < headers.length ? headers[i + 1].index : raw.length;
const sectionContent = raw.substring(header.endIndex, nextIndex).trim();
// Extract version from header like "2026年2月19日(v0.1.9" or "12月25日(v0.1.2-beta5"
// Extract the version from headers that use localized date formats with the version in parentheses
const versionMatch = header.fullMatch.match(/[(]v?(\d+\.\d+(?:\.\d+)?(?:-[a-zA-Z0-9.]+)?)[)]/);
if (!versionMatch) continue;
@@ -77,8 +77,8 @@ function parseChangelog(raw) {
* Split section content into English and Chinese parts.
*/
function splitBilingual(text) {
// Try to find English/中文 markers
// Patterns: "English:" / "中文:" or "中文:"
// Try to find the English and Chinese section markers
// Patterns: "English:" and the localized Chinese marker with either colon form
const enMarkerRegex = /^English\s*[:]/im;
const zhMarkerRegex = /^中文\s*[:]/im;
+7 -7
View File
@@ -4,22 +4,22 @@ import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
// 获取当前目录
// Get the current directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// 获取项目根目录 (webview 的父目录)
// Get the project root directory (the parent of webview)
const projectRoot = path.resolve(__dirname, '../..');
const buildGradlePath = path.join(projectRoot, 'build.gradle');
// 读取 build.gradle 文件
// Read the build.gradle file
const buildGradleContent = fs.readFileSync(buildGradlePath, 'utf8');
// 提取版本号
// 查找类似 version = '0.1.0-beta3' 这样的行
// Extract the version number
// Look for a line like: version = '0.1.0-beta3'
let versionMatch = buildGradleContent.match(/^version\s*=\s*'(.+)'$/m);
if (!versionMatch) {
// 如果上面的正则失败,尝试另一种方式
// If the regex above fails, try a fallback approach
const lines = buildGradleContent.split('\n');
const versionLine = lines.find(line => line.trim().startsWith('version ='));
if (versionLine) {
@@ -37,7 +37,7 @@ if (!versionMatch) {
const version = versionMatch[1];
console.log(`Found version: ${version}`);
// 创建版本文件供 webview 使用
// Create the version file for the webview
const versionDir = path.join(__dirname, '../src/version');
if (!fs.existsSync(versionDir)) {
fs.mkdirSync(versionDir, { recursive: true });
@@ -4,7 +4,7 @@ import type { PromptConfig, PromptScope } from '../../../types/prompt';
import styles from './style.module.less';
interface PromptScopeSectionProps {
/** Section title (e.g., "全局提示词" or "项目提示词 - ProjectName") */
/** Section title (e.g., "Global Prompts" or "Project Prompts - ProjectName") */
title: string;
/** Prompt scope (global or project) */
scope: PromptScope;
+6 -6
View File
@@ -1,7 +1,7 @@
import { STORAGE_KEYS } from '../types/provider';
/**
* Claude 模型映射配置。
* Claude model mapping configuration.
*/
export interface ClaudeModelMapping {
main?: string;
@@ -12,7 +12,7 @@ export interface ClaudeModelMapping {
}
/**
* 读取 Claude 模型映射。
* Read the Claude model mapping.
*/
export function readClaudeModelMapping(): ClaudeModelMapping {
try {
@@ -28,14 +28,14 @@ export function readClaudeModelMapping(): ClaudeModelMapping {
}
/**
* 判断映射里是否至少包含一个有效模型值。
* Check whether the mapping contains at least one valid model value.
*/
function hasMappingValue(mapping: ClaudeModelMapping): boolean {
return Object.values(mapping).some(value => value && value.trim().length > 0);
}
/**
* 写入 Claude 模型映射,并主动通知同 tab 监听器刷新。
* Write the Claude model mapping and proactively notify listeners in the same tab to refresh.
*/
export function writeClaudeModelMapping(mapping: ClaudeModelMapping): void {
try {
@@ -45,11 +45,11 @@ export function writeClaudeModelMapping(mapping: ClaudeModelMapping): void {
localStorage.removeItem(STORAGE_KEYS.CLAUDE_MODEL_MAPPING);
}
// 同 tab 的 localStorage 写入不会触发原生 storage 事件,这里手动补发一次。
// localStorage writes in the same tab do not trigger the native storage event, so dispatch one manually here.
window.dispatchEvent(new CustomEvent('localStorageChange', {
detail: { key: STORAGE_KEYS.CLAUDE_MODEL_MAPPING },
}));
} catch {
// localStorage 不可用或写入失败时静默降级
// Gracefully degrade when localStorage is unavailable or the write fails
}
}
+2 -2
View File
@@ -17,8 +17,8 @@ export function getMessageKey(message: ClaudeMessage, index: number): string {
* Returns the combined content: "command-message content command-args content"
*
* Example:
* Input: "<command-message>aimax:auto</command-message>\n<command-name>/aimax:auto</command-name>\n<command-args>你好啊</command-args>"
* Output: "aimax:auto 你好啊"
* Input: "<command-message>aimax:auto</command-message>\n<command-name>/aimax:auto</command-name>\n<command-args>hello there</command-args>"
* Output: "aimax:auto hello there"
*/
export function extractCommandMessageContent(text: string): string {
if (!text) return text;