Skip to content
2 changes: 2 additions & 0 deletions .claude/rules/config-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ paths:
- 专项复杂字段由 profile-editor 子组件维护,例如 Permissions、Sandbox、Hooks、Marketplace、Enabled Plugins、Status Line。
- 结构化设置分区的官方文档入口在 `StructuredSettingsSections.tsx`,新增分区时同步文档路径、i18n 和错误聚合。
- 复杂编辑器必须避免首次挂载 no-op writeback。尤其是 accordion 内懒挂载组件,语义等价时不要调用 `onChange`。
- 同一语义存在两层存储时(`env.CLAUDE_CODE_EFFORT_LEVEL` 与顶层 `effortLevel`),编辑器必须按卡片的回退顺序读取:`env 覆盖 -> 供应商默认 -> 同义顶层键`,并在写回时清掉同义顶层键。只读写 env 会出现「卡片有值、编辑器未设置」,写回后残留的顶层键还会继续遮蔽新值(`SettingsFieldDefinition.legacyTopLevelKey`)。
- `ProfileEditor` 的 dirty 判断仍依赖 JSON 结构比较;局部编辑器写回时要保留未管理字段和 key 语义,避免只重建自己认识的字段。

## 插件与 Marketplace
Expand All @@ -75,6 +76,7 @@ paths:
## 权限与状态行

- 权限编辑器只管理 `defaultMode`、`disableBypassPermissionsMode`、`allow`、`deny`、`ask`、`additionalDirectories`;写回时保留其它顶层字段,例如 `disableAutoMode`。
- 权限(`permission-presets.ts`)与沙箱(`sandbox-presets.ts`)的推荐预设逐条镜像维护者 dotfiles 的 `claude/settings.json` 对应段落,不额外追加项目自造的加固项;改任意一侧时同步另一侧和 `ProfileEditor.test.tsx` 断言。
- 修复权限 dirty 问题时优先做局部语义比较,不要扩大到全局 dirty 系统。
- 状态行默认脚本按平台分发:非 Windows 用 `src-tauri/resources/statusline/default.sh`(Bash,依赖 jq),Windows 用 `src-tauri/resources/statusline/default.ps1`(PowerShell,免 jq)。安装走后端 `install_status_line_preset`:Windows 写入 `~/.claude/statusline.ps1` 并把 `command` 设为绝对正斜杠**且加引号**的 `powershell -NoProfile -ExecutionPolicy Bypass -File "..."`(用户名含空格时不加引号会截断参数);两份脚本功能需保持对齐。
- 两份脚本源文件都**不带 BOM**;Windows 落盘时由 `config.rs::expected_status_line_script()` 前置 UTF-8 BOM。Windows PowerShell 5.1 读取无 BOM 的 `.ps1` 时按系统代码页(简中 CP936)解码,UTF-8 中文注释错位后残留的悬空 lead byte 会吞掉行尾换行,使下一行代码并入注释并触发 `ParserError`,状态行整行无输出。给源文件加 BOM 会变成双 BOM,Bash 脚本加 BOM 会让 shebang 失效——两者都不要做。
Expand Down
2 changes: 1 addition & 1 deletion docs/user-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ The configuration editor on the right is divided into several sections.
- **Basic information**: name (required), description, optional provider (selecting one automatically fills in the connection endpoint and model mapping).
- **Authentication**: the authentication key is written to `env.ANTHROPIC_AUTH_TOKEN`; the API endpoint is written to `env.ANTHROPIC_BASE_URL`, and when it is not set, the model test uses the official Anthropic endpoint.
- **Models and behavior**: the default model (an editable dropdown whose candidates come from the current provider's models, or you can type a custom model), the effort level (`auto`/`low`/`medium`/`high`/`xhigh`/`max`), the Opus / Sonnet / Haiku default models (also editable dropdowns), the Subagent model, the reply language, and the output style.
- **Common options**: override common Claude Code switches such as deep thinking, thinking summaries, Fast Mode, disable Hooks, disable AI attribution, LSP tools, Tool Search, the new Init, no flicker, and Agent Teams. See the editor for the full list.
- **Common options**: override common Claude Code switches such as deep thinking, thinking summaries, Fast Mode, disable Hooks, disable AI attribution, LSP tools, Tool Search, the new Init, no flicker, subprocess credential scrubbing, and Agent Teams. See the editor for the full list.
- **Environment variables**: maintain `env` keys other than authentication and common options. Duplicate keys, invalid JSON, or an unsaved row edit will block saving.
- **Permissions**: default mode, disable `bypassPermissions`, allow / deny / ask rules, additional directories, and recommended rule presets.
- **Sandbox**: can be enabled or disabled, with recommended presets to add; complex configurations can switch to JSON mode.
Expand Down
2 changes: 1 addition & 1 deletion docs/user-manual.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ Skills 对应 `~/.claude/skills/<id>/SKILL.md`。启用的 Skill 保存在 `~/.c
- **基础信息**:名称(必填)、描述、可选供应商(选择后自动带入连接地址与模型映射)。
- **认证**:认证密钥写入 `env.ANTHROPIC_AUTH_TOKEN`;API 地址写入 `env.ANTHROPIC_BASE_URL`,未设置时模型测试使用 Anthropic 官方地址。
- **模型与行为**:默认模型(可输入下拉框,候选取自当前供应商的模型,也可手工输入自定义模型)、努力级别(`auto`/`low`/`medium`/`high`/`xhigh`/`max`)、Opus / Sonnet / Haiku 默认模型(同为可输入下拉框)、Subagent 模型、回复语言、输出风格。
- **常用选项**:覆盖深度思考、Thinking 摘要、Fast Mode、禁用 Hooks、禁用 AI 署名、LSP 工具、Tool Search、新版 Init、无闪烁、Agent Teams 等 Claude Code 常用开关。具体可在编辑器内查看。
- **常用选项**:覆盖深度思考、Thinking 摘要、Fast Mode、禁用 Hooks、禁用 AI 署名、LSP 工具、Tool Search、新版 Init、无闪烁、子进程凭据清理、Agent Teams 等 Claude Code 常用开关。具体可在编辑器内查看。
- **环境变量**:维护除认证和常用选项外的 `env` 键。重复键、非法 JSON 或未保存的行编辑会阻止保存。
- **权限**:默认模式、禁用 `bypassPermissions`、允许 / 拒绝 / 询问规则、附加目录、推荐规则预设。
- **Sandbox**:可启用或关闭,可添加推荐预设;复杂配置可切换 JSON 模式。
Expand Down
35 changes: 29 additions & 6 deletions src/components/ProfileEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -410,16 +410,23 @@ const ProfileEditor = forwardRef<ProfileEditorHandle, ProfileEditorProps>(functi
// providerDefault 是 provider 提供的继承默认,effectiveValue 是最终生效值。
const override = readEnvString(settings, field.envKey);
const providerDefault = readProviderEnvDefault(field.envKey) || field.defaultValue || "";
// 同义顶层键(如 effortLevel)是 schema 规范字段,Claude Code 的 /effort 会写入:
// 与配置卡片的回退顺序一致(env 覆盖 -> 供应商默认 -> 顶层键),避免两边显示不同的值
const legacyValue = field.legacyTopLevelKey
? readString(settings[field.legacyTopLevelKey])
: "";
const source: "override" | "inherited" | "unset" = override
? "override"
: providerDefault
? "inherited"
: "unset";
: legacyValue
? "override"
: "unset";
return {
mappedToEnv: true,
value: override,
value: override || (!providerDefault ? legacyValue : ""),
providerDefault,
effectiveValue: override || providerDefault,
effectiveValue: override || providerDefault || legacyValue,
source,
};
}
Expand All @@ -441,13 +448,29 @@ const ProfileEditor = forwardRef<ProfileEditorHandle, ProfileEditorProps>(functi
return readString(settings[field.key]);
}

function setBehaviorEnvValue(
currentSettings: Record<string, unknown>,
field: SettingsFieldDefinition,
value: string,
) {
if (!field.envKey) {
return currentSettings;
}
let next = setEnvString(currentSettings, field.envKey, value);
if (field.legacyTopLevelKey) {
// 单字段与批量写回都清掉同义顶层键,避免旧值在之后的回退中重新出现。
next = setTopLevelString(next, field.legacyTopLevelKey, "");
}
return next;
}

function handleMappedFieldChange(
field: SettingsFieldDefinition,
value: string,
_mappedToEnv: boolean,
) {
if (field.envKey) {
applySettings(setEnvString(settings, field.envKey, value));
applySettings(setBehaviorEnvValue(settings, field, value));
return;
}
handleSimpleFieldChange(field, value);
Expand Down Expand Up @@ -658,7 +681,7 @@ const ProfileEditor = forwardRef<ProfileEditorHandle, ProfileEditorProps>(functi
}
const state = readBehaviorFieldState(field);
if (state.source === "override" && state.providerDefault) {
return setEnvString(acc, field.envKey, "");
return setBehaviorEnvValue(acc, field, "");
}
return acc;
}, settings);
Expand All @@ -673,7 +696,7 @@ const ProfileEditor = forwardRef<ProfileEditorHandle, ProfileEditorProps>(functi
}
const state = readBehaviorFieldState(field);
if (state.source === "inherited" && state.providerDefault) {
return setEnvString(acc, field.envKey, state.providerDefault);
return setBehaviorEnvValue(acc, field, state.providerDefault);
}
return acc;
}, settings);
Expand Down
Loading
Loading