refactor(settings): 重构设置页分区并统一分组设置的覆盖交互 - #576
Conversation
- 路由策略并入亲和分区(路由与调度),validation_interval 移出超时分组并入重试/凭据健康, models_dev_auto_sync_enabled 从请求转发移至数据与维护,header_rules 与 inject_usage_options 合并为上游请求改写;6 个分区拆为 7 个语义更清晰的分区。 - 新增 SettingRow 承载默认态单行展示、覆盖态才展开控件与说明文字,默认态徽标与操作按钮收敛, 避免逐字段重复常驻的说明文案。
- SettingRow 从设置页移至 components/config,替换分组设置页运行时区块里的 RuntimeOverrideRow,行为对齐:默认态收敛徽标/按钮、覆盖态才展开控件与说明。 - RuntimeOverrideRow 无剩余引用,一并删除。 - 顺带修正分组页 inject_usage_options 折叠态展示 help 文案而非当前值的问题, 改为展示已启用/已禁用,说明文字移入 help 提示。 - 请求亲和沿用原有 SegmentedControl 三态控件,未纳入本次迁移。
- 提示统一用已有 AppTooltip 组件,替换原生 title;且不论是否覆盖,说明都只在 标签旁以 tooltip 展示,不再于覆盖态额外常驻一份。 - 覆盖/恢复默认按钮从行尾移到值后面,改回可见的按钮样式,不再靠悬停淡入。 - 覆盖态不再叠加“显式覆盖”徽标,仅保留左侧强调色与底色区分;待恢复/环境变量 锁定两种状态的徽标保留。 - 路由策略改用 SegmentedControl 二选一切换,替换下拉选择,和代理等控件观感一致。 - 各分区行列表之间补上小间距,避免行与行贴在一起。
文字按钮换成小方形图标按钮:覆盖用 PencilLine,恢复默认用 RotateCcw,配色沿用 原有 action/warning 两种 tone;按钮不再显示文字,改为 hover/聚焦时通过 AppTooltip 展示对应文案,无障碍上保留 aria-label。
去掉常驻边框和背景色,图标默认裸露显示,与旁边的说明提示图标风格保持一致; 悬停/聚焦时才出现对应 tone 的浅色背景。
未覆盖态(含待恢复默认)的“覆盖”图标恢复为鼠标悬停/键盘聚焦到该行才显示, 平时隐藏;已覆盖态的“恢复默认”图标保持常驻可见,因为该行本身已有强调色和 底色区分,不需要靠图标常驻来提示。窄屏和纯触屏环境下始终显示,避免无法触发。
移除 inject_usage_options 配置,改为始终注入: - 该开关只在「OpenAI Completions + 原生透传 + 流式」这一条路径生效(转换路径的 usage 来自 SDK,Anthropic/Gemini 原生流自带 usage),默认即为开启,暴露为用户 配置的收益远低于理解成本。 - 后端删除设置键、分组覆盖与渠道约束校验,IncludeUsage 固定为 ObserveUsage; 新增迁移 0008 清理 system_settings 行与分组 overrides 中的历史键,否则解析器 会以 "unknown runtime setting" 拒绝整份配置。 - 前端删除两处设置行、资源投影字段与三语文案。 设置页与分组设置页的控件: - 新增 --setting-control-height,并在 .setting-row/.setting-block 上覆盖 --control-compact/xs/sm,让输入框、切换控件、下拉统一小一档;变量继承进子组件, 不需要和组件自身 scoped 样式比优先级。 - 固定行内容高度,折叠态与覆盖态不再有高度差,点开编辑时行不会跳动。 - 块级说明(CORS 策略、下游响应头规则)从常驻小字改为标签旁 tooltip,与设置行一致。 分组设置页删除只读的渠道展示:顶部已有渠道信息,此处重复且不可编辑。
- 反引号是 MySQL/SQLite 语法,PostgreSQL 解析 `key` = ? 直接报 syntax error, 导致启动中断。改用 GORM 结构体条件,由驱动按方言自行加引号。 - 往 json 列写 []byte 时 PostgreSQL 驱动按 bytea 处理,报 invalid input syntax for type json。改为写字符串。 已在 SQLite / PostgreSQL 16 / MySQL 8 三种方言上实跑升级路径:注入历史的 system_settings 行与分组 overrides 后启动,确认键被清除、分组只剩其余覆盖、 迁移正常记账。
- 分组页控件尺寸未跟着变小,是因为上一轮的尺寸变量覆盖只挂在 .setting-row/ .setting-block 上;分组页的姓名/验活模型输入框、权重与请求亲和的 SegmentedControl 都不在这两个类名内。改为挂在 .settings__content/.group-settings__content 内容区 (不含侧边导航与保存栏),覆盖 --control-compact/xs/sm/md 四个尺寸变量,让设置页 和分组页的所有交互控件统一收敛到 26px。 - "上游请求改写"分区被删除,header_rules 合并进浏览器访问分区,与 CORS、 下游响应头规则放在一起(顺序:CORS → 上游请求头规则 → 下游响应头规则); 该分区原本只剩 header_rules 一项(inject_usage_options 上一轮已删除), 单独占一个侧边菜单已经没有意义。 - 请求头/响应头命名统一:全局设置页"全局 Header Rules"改名"上游请求头规则", 与已有的"下游响应头规则"对称;分组页"HeaderRules"同步改名"上游请求头规则"。 新分区标题定为"Header 与跨域"。 - 清理合并后失效的 settings.headers.* 键(title/currentPublishedRuleCount/ defaultSource/overrideSource/pendingRestoreSource/override/restoreDefault/ inherited/resetPending/replacementWarning/securityNotice),改用已有的 settings.runtime.* 通用状态词汇,与 CORS、下游响应头规则保持一致。 已用真实浏览器验证:分区合并后的顺序与文案、校验横幅跳转到正确分区并定位到 非法输入框、分组页控件高度与设置页一致。
请求亲和由三态 SegmentedControl 改为 SettingRow 覆盖/恢复默认模式; 上游请求头规则由 details 折叠改为 SettingBlock,并补上独立的覆盖开关, 与设置页的交互模式完全一致;同步移除废弃的展开态路由状态与文案。
分组已保存覆盖值后,草稿中点击恢复默认会读到仍是旧覆盖值的 effective 快照,导致视觉上"看起来没变"。为出站代理、超时/次数类、请求亲和、 上游请求头规则补充待恢复态:展示占位文案与徽标,保存后才回填真实的 全局生效值,与设置页面的重置反馈保持一致。
复用参数覆盖规则里模型输入的 datalist 模式,把分组当前模型作为候选, 输入即过滤;候选取上游模型 ID(验活按该值请求上游),别名作为副标题 展示。后端不限制该值,仍保留自由输入。
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: QUIET Plan: Team Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review. 📝 WalkthroughWalkthrough本次变更移除了 Merge Risk: 🔵 Low · up to This change removes the usage-injection setting and restructures settings interactions. The remaining risk is limited to a rare settings deep-link navigation race that may scroll a user back to an earlier section; it does not affect persisted configuration or gateway behavior. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 12.12% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 33 functions across 21 files. (3 skipped: 3 unsupported.) Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🟡 Changes recommended
The new navigation route parsing drops backward compatibility for existing deep links and there are a few behavioral/operational issues in the new shared settings components and initial scroll retry that should be addressed before merging.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR refactors the Settings and Group Settings UIs to share a unified “override / restore default” interaction model (via new shared components), reorganizes Settings into semantic sections with improved deep-link behavior, and removes the deprecated inject_usage_options setting end-to-end (including a storage migration).
Changes:
- Introduce shared
SettingRow/SettingBlockcomponents and refactor Settings + Group Settings to use consistent override/pending-restore interactions. - Repartition global Settings sections (routing/connection/reliability/headers&cors/data maintenance/system) and add a first-render deep-link scroll retry.
- Remove
inject_usage_optionsfrom UI, APIs, state resolution, and persistency; add migration0008_remove_inject_usage_options.
File summaries
| File | Description |
|---|---|
| web/src/styles/tokens.css | Add unified --setting-control-height token for settings controls. |
| web/src/styles/components.css | Scope smaller control sizing to settings content areas. |
| web/src/i18n/locales/zh-CN/settings.ts | Update Settings section labels/descriptions; remove inject_usage i18n. |
| web/src/i18n/locales/zh-CN/group.ts | Align Group settings labels/help; add pending-restore copy; remove inject_usage i18n. |
| web/src/i18n/locales/ja-JP/settings.ts | Same as zh-CN Settings i18n updates. |
| web/src/i18n/locales/ja-JP/group.ts | Same as zh-CN Group i18n updates. |
| web/src/i18n/locales/en-US/settings.ts | Same as zh-CN Settings i18n updates. |
| web/src/i18n/locales/en-US/group.ts | Same as zh-CN Group i18n updates. |
| web/src/features/settings/SystemInfoSection.vue | Match heading typography to new section title token. |
| web/src/features/settings/SettingsView.vue | Wire new sections; adjust dirty/valid tracking; add deep-link retry scroll. |
| web/src/features/settings/settings-route.ts | Rename/replace section IDs and default section behavior. |
| web/src/features/settings/settings-patch.ts | Ensure header rules are cloned on override enable; remove inject_usage handling. |
| web/src/features/settings/RuntimeSettingsSection.vue | Remove legacy forwarding section implementation. |
| web/src/features/settings/RoutingSettingsSection.vue | New routing section using SettingRow with override semantics. |
| web/src/features/settings/ReliabilitySettingsSection.vue | New retries/health section using SettingRow. |
| web/src/features/settings/LogsMaintenanceSection.vue | Remove legacy logs section implementation. |
| web/src/features/settings/GlobalHeaderRulesSection.vue | Remove standalone header rules section (moved into BrowserAccess). |
| web/src/features/settings/DataMaintenanceSection.vue | New data/maintenance section (retention + models.dev sync). |
| web/src/features/settings/ConnectionSettingsSection.vue | New connection/timeouts section (proxy + timeouts). |
| web/src/features/settings/BrowserAccessSection.vue | Refactor CORS + header rules into SettingBlock; add header-rules validity plumbing. |
| web/src/features/settings/AffinitySettingsSection.vue | Remove legacy affinity section implementation. |
| web/src/features/groups/settings/GroupSettingsTab.vue | Refactor Group overrides to shared components; add explicit header-rules override toggle; remove inject_usage override. |
| web/src/features/groups/settings/GroupSettingsBaseForm.vue | Replace validation model input with datalist-based “search or enter” UX. |
| web/src/features/groups/settings/group-settings-patch.ts | Remove inject_usage from group overrides cloning. |
| web/src/features/groups/group-route.ts | Remove headers=expanded route state for group settings. |
| web/src/composables/use-section-navigation.ts | Allow callers to override scroll behavior per selection. |
| web/src/components/config/SettingRow.vue | New shared single-setting row component. |
| web/src/components/config/SettingBlock.vue | New shared multi-field settings block component. |
| web/src/components/config/RuntimeOverrideRow.vue | Remove legacy runtime override row component. |
| web/src/app/resources/settings.ts | Remove inject_usage from Settings DTO modeling/projection. |
| web/src/app/resources/groups.ts | Remove inject_usage from Group DTO modeling/projection. |
| web/src/api/control/types.ts | Remove inject_usage from API types. |
| internal/storage/migrations/0008_remove_inject_usage_options.go | New migration removing system row + stripping group overrides key. |
| internal/storage/migration.go | Register migration 0008. |
| internal/storage/migration_test.go | Include 0008 in ordered registry assertion. |
| internal/storage/migration_recovery_test.go | Adjust recovery test expectations to exclude future migrations. |
| internal/storage/db_test.go | Update expected migration ledger IDs to include 0008. |
| internal/state/snapshot.go | Remove InjectUsageOptions from compiled group view snapshot. |
| internal/state/runtime_settings.go | Remove inject_usage key + resolution/validation; default is now unconditional include_usage behavior. |
| internal/state/runtime_settings_test.go | Remove inject_usage-related runtime settings tests. |
| internal/gateway/stream_integration_test.go | Update streaming test to assert include_usage is always requested upstream. |
| internal/gateway/handler_test.go | Update “frozen snapshot across retry” test to use a different setting. |
| internal/gateway/execution_forward.go | Always include usage when observation is enabled (no per-group gate). |
| internal/control/settings.go | Remove inject_usage from settings API response mapping. |
| internal/control/settings_test.go | Remove inject_usage from control/settings tests. |
| internal/control/group_settings.go | Remove inject_usage cross-channel constraint validation. |
| internal/control/group_settings_test.go | Remove inject_usage from group settings tests and expectations. |
| internal/control/group_models_test.go | Update model update tests to stop asserting inject_usage persistence. |
| internal/control/group_detail.go | Remove inject_usage from group effective config response. |
Review details
- Files reviewed: 49/49 changed files
- Comments generated: 5
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (5)
web/src/features/settings/BrowserAccessSection.vue-62-66 (1)
62-66: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win待恢复态的取值规则未同步到响应头规则块。
第 62-66 行让
header_rules在headerRulesPendingRestore为真时取草稿值。setSettingsOverride在取消覆盖时把草稿值重置为{ set: {}, remove: [] }(web/src/features/settings/settings-patch.ts第 99-100 行),所以取消覆盖后请求头列表与headerRuleCount会立即清空,不再回显旧值。第 73-77 行的
responseRules没有包含responseRulesPendingRestore。取消响应头覆盖后,列表和计数仍显示base中的旧覆盖值。两个同类区块的待恢复表现不一致。请把同一规则应用到
responseRules。🐛 建议的对齐修改
const responseRules = computed(() => - responseRulesOverridden.value + responseRulesOverridden.value || responseRulesPendingRestore.value ? props.draft.values.response_header_rules : props.base.settings.values.response_header_rules, )web/src/features/settings/BrowserAccessSection.vue-465-465 (1)
465-465: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick winSensitive Data Exposure (CWE-522): Insufficiently Protected Credentials
Reachability: Internal · Exploitability: Theoretical
补充请求头凭据的明文存储警告。
show-notice="false"会隐藏HeaderRulesEditor的存储提示。三种语言的settings.headers.description均未说明请求头值会以明文持久化。请在三种语言中补充该风险说明,或恢复内置提示。web/src/features/settings/SettingsView.vue-242-242 (1)
242-242: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win阻止延迟定位覆盖后续导航。
此定时器始终滚动到首次捕获的
target。如果用户在 120ms 内选择其他分区,或路由已切换到其他分区,回调仍会把页面移回旧分区。执行回调前检查
activeSection.value或当前路由是否仍对应target。组件卸载时也应清除该定时器。web/src/features/settings/ReliabilitySettingsSection.vue-78-82 (1)
78-82: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win将可靠性设置的空输入转换为
Number.NaN当
setPolicyCount或setValidationInterval收到空字符串时,Number(value)会产生0。retry_count和blacklist_threshold的0可通过isValidNonNegativeInteger并保存;validation_interval的0会被判为无效,但输入仍会回写为0。请在这两个写入点使用value.trim() === '' ? Number.NaN : Number(value),其中setPolicyCount同时覆盖retry_count和blacklist_threshold。web/src/features/settings/ConnectionSettingsSection.vue-118-122 (1)
118-122: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win在
ConnectionSettingsSection.vue中处理空的超时输入。
AppTextInput通过@update:model-value调用setTimeoutValue,受控值来自String(draft.values[key])。当用户清空输入框时,Number('')会将0写入连接超时设置,随后受控输入显示0。ReliabilitySettingsSection.vue不处理这些设置,修改该文件不会修复此路径。🐛 建议的修改
function setTimeoutValue(key: TimeoutSettingKey, value: string): void { const draft = cloneDraft() - draft.values[key] = Number(value) + draft.values[key] = value.trim() === '' ? Number.NaN : Number(value) publish(key, draft) }
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: QUIET
Plan: Team
Run ID: 0c04ff11-4b24-4809-952d-8d79e9ef5e10
📒 Files selected for processing (49)
internal/control/group_detail.gointernal/control/group_models_test.gointernal/control/group_settings.gointernal/control/group_settings_test.gointernal/control/settings.gointernal/control/settings_test.gointernal/gateway/execution_forward.gointernal/gateway/handler_test.gointernal/gateway/stream_integration_test.gointernal/state/runtime_settings.gointernal/state/runtime_settings_test.gointernal/state/snapshot.gointernal/storage/db_test.gointernal/storage/migration.gointernal/storage/migration_recovery_test.gointernal/storage/migration_test.gointernal/storage/migrations/0008_remove_inject_usage_options.goweb/src/api/control/types.tsweb/src/app/resources/groups.tsweb/src/app/resources/settings.tsweb/src/components/config/RuntimeOverrideRow.vueweb/src/components/config/SettingBlock.vueweb/src/components/config/SettingRow.vueweb/src/composables/use-section-navigation.tsweb/src/features/groups/group-route.tsweb/src/features/groups/settings/GroupSettingsBaseForm.vueweb/src/features/groups/settings/GroupSettingsTab.vueweb/src/features/groups/settings/group-settings-patch.tsweb/src/features/settings/AffinitySettingsSection.vueweb/src/features/settings/BrowserAccessSection.vueweb/src/features/settings/ConnectionSettingsSection.vueweb/src/features/settings/DataMaintenanceSection.vueweb/src/features/settings/GlobalHeaderRulesSection.vueweb/src/features/settings/LogsMaintenanceSection.vueweb/src/features/settings/ReliabilitySettingsSection.vueweb/src/features/settings/RoutingSettingsSection.vueweb/src/features/settings/RuntimeSettingsSection.vueweb/src/features/settings/SettingsView.vueweb/src/features/settings/SystemInfoSection.vueweb/src/features/settings/settings-patch.tsweb/src/features/settings/settings-route.tsweb/src/i18n/locales/en-US/group.tsweb/src/i18n/locales/en-US/settings.tsweb/src/i18n/locales/ja-JP/group.tsweb/src/i18n/locales/ja-JP/settings.tsweb/src/i18n/locales/zh-CN/group.tsweb/src/i18n/locales/zh-CN/settings.tsweb/src/styles/components.cssweb/src/styles/tokens.css
💤 Files with no reviewable changes (16)
- web/src/features/groups/settings/group-settings-patch.ts
- web/src/api/control/types.ts
- web/src/features/settings/RuntimeSettingsSection.vue
- internal/control/settings.go
- web/src/features/settings/LogsMaintenanceSection.vue
- internal/state/snapshot.go
- internal/state/runtime_settings_test.go
- internal/control/group_detail.go
- web/src/app/resources/settings.ts
- web/src/features/settings/AffinitySettingsSection.vue
- web/src/features/settings/GlobalHeaderRulesSection.vue
- web/src/components/config/RuntimeOverrideRow.vue
- web/src/features/groups/group-route.ts
- internal/control/group_settings.go
- web/src/app/resources/groups.ts
- internal/state/runtime_settings.go
Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.
components.css 冲突为双方各自在文件末尾追加新块(main 的 .filterable-value 与本分支的设置页控件尺寸变量覆盖),语义不重叠,合并时保留双方。
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
web/src/i18n/locales/en-US/group.ts (1)
337-338: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win改进
pendingRestoreSource的英文文案。
Pending inherit不是自然的英文表达。它也不能清楚说明保存后才会继承全局设置。请改为
Pending inheritance或Will inherit after save,并与resetPending的说明保持一致。
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: QUIET
Plan: Team
Run ID: 20d6fc1e-146a-48b4-ae84-12725160fbd6
📒 Files selected for processing (5)
web/src/api/control/types.tsweb/src/i18n/locales/en-US/group.tsweb/src/i18n/locales/ja-JP/group.tsweb/src/i18n/locales/zh-CN/group.tsweb/src/styles/components.css
Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.
- 外部数据库契约里的迁移账本断言硬编码到 0007,只在 PostgreSQL/MySQL 下 执行,本地 SQLite 会跳过,导致 0008 落库后仅 CI 报错 - 下游响应头规则的待恢复态未取草稿值,取消覆盖后仍回显旧规则,与同区的 上游请求头规则表现不一致 - 连接与可靠性分区的数字输入清空后写入 0,其中重试次数与拉黑阈值的 0 合法, 会被静默保存;改为与路由分区一致按 NaN 处理 - 修正 pendingRestoreSource 的英文文案
Review 处理说明已修复(a54e52f2)
未采纳
其余 Copilot 意见(迁移分批查询、 |
关联 Issue / Related Issue
无 / N/A(来自使用过程中整理的交互问题)
变更内容 / Change Content
围绕「设置页」和「分组设置页」做一轮交互与视觉统一,让两个页面共用同一套覆盖/继承的表达方式,并顺带移除一个没有配置价值的开关。
1. 设置页重构
SettingRow(单值设置行)与SettingBlock(多字段规则编辑块)两个共享组件替换RuntimeOverrideRow,设置页与分组设置页共用。PencilLine/RotateCcw)+ tooltip;未覆盖行的「覆盖」图标悬停才出现。--setting-control-height),折叠态与编辑态行高一致,点开编辑不再跳动。2. 分组设置页对齐同一套交互
SegmentedControl(继承/开启/关闭)→ 与设置页一致的覆盖模式(折叠态显示生效值,覆盖后才出现开关,后面跟恢复默认)。<details>折叠 →SettingBlock,并补上此前缺失的独立覆盖开关(原先是「一编辑即写入覆盖」,没有显式启停)。3. 修复
header_rules覆盖开关往返会丢掉已有规则(开启覆盖时未从已发布值回填)。4. 移除
inject_usage_options该开关只对 OpenAI 兼容格式生效,关闭也换不到实际收益,配置价值不足;改为始终开启,后端类型/解析/校验/网关逻辑与前端资源、i18n 一并移除。
数据迁移 / Migration
新增
0008_remove_inject_usage_options:删除system_settings中该行,并从每个分组的overridesJSON 里剥离该键。已在 SQLite、PostgreSQL 16、MySQL 8 上注入历史数据实测——其中两个只在 PostgreSQL 上暴露的方言问题(反引号标识符、[]byte写入json列)已随1f67db7a修复。验证 / Verification
make check全绿(gofmt /go mod tidy -diff/ vet / lint / format / web build / go build / 全量 Go 测试)。DATA_DIR、AUTH_KEY,不接触真实数据)里逐项走查:覆盖与恢复默认往返、待恢复态、Header 规则编辑器启停与规则计数、验活模型候选/自由输入/清空、保存落库结果。Reviewer 须知 / Notes
web/src/styles/components.css与当前main有一处冲突:双方各自在文件末尾追加了新块(main是.filterable-value,本分支是控件尺寸变量覆盖),语义不重叠,合并时保留双方即可。internal/dialect/openai_usage_options.go及其测试在本次移除后已无调用方,属于死代码,本 PR 未一并删除,可后续单独清理。自查清单 / Checklist
make check,或在说明中写明无法运行的原因和未验证范围。 / I ranmake check, or documented why it could not run and what remains unverified.