fix(subagent): 实验开关改为滑动开关,修复开启态因 bg-primary 未定义而隐形

- Toggle 开启态改用已定义的 bg-blue-600(tailwind 配置无 primary 背景色,
  导致开启态滑轨透明、白色滑块在白底上不可见)
- 子代理开关移至模型下拉之后,统一使用滑动开关
This commit is contained in:
KimiSwitch Dev committed 2026-08-06 01:56:35 +08:00
1 parent 8aed04898c
commit 90cc4a16d2
3 files changed
+86 -21

No files matched your search

+18
View File
@@ -0,0 +1,18 @@
## v0.7.1
### 实验功能设置:子代理模型配置
- 新增「子代理模型(次主力模型)」设置:选择次主力模型后,子代理默认绑定该模型,不再继承主模型
- 仅保存模型引用,子代理直接继承所选模型的上下文长度与思考模式,无需补丁配置
- 界面只读展示继承的上下文长度与思考模式
### 仪表盘用量统计
- 识别子代理请求(`__secondary__`):归为「子代理模型(估算)」独立展示,并缓存次主力模型定价,删除配置后历史记录仍稳定估算
- 自定义网关模型(未收录 models.dev)按官方同名模型跨 provider 匹配价格,不再落入兜底估算
### Downloads by platform
- **Windows**: `.msi` installer
- **macOS**: `.dmg` (unsigned — first launch needs right-click → Open) + `.app`
- **Linux**: `.deb` / `.AppImage` / `.rpm`
+35 -21
View File
@@ -15,7 +15,7 @@ import {
setSecondaryModelOnly, setSecondaryModelOnly,
type ExperimentalFlagDef, type ExperimentalFlagDef,
} from "../lib/subagent-settings"; } from "../lib/subagent-settings";
import { Card, Checkbox } from "./ui/controls"; import { Card, Toggle } from "./ui/controls";
interface SubagentSettingsPageProps { interface SubagentSettingsPageProps {
/** config.raw_other — hosts the `[experimental]` and `[secondary_model]` sections. */ /** config.raw_other — hosts the `[experimental]` and `[secondary_model]` sections. */
@@ -71,6 +71,7 @@ export function SubagentSettingsPage({
: isFlagLockedByEnv(env, secondaryFlag) : isFlagLockedByEnv(env, secondaryFlag)
? forcedEnvValue(env, secondaryFlag) ? forcedEnvValue(env, secondaryFlag)
: flags["secondary-model"] === true; : flags["secondary-model"] === true;
const secondaryLocked = masterOn || isFlagLockedByEnv(env, secondaryFlag);
const aliasList = Object.keys(models).sort(); const aliasList = Object.keys(models).sort();
@@ -127,10 +128,10 @@ export function SubagentSettingsPage({
🔒 {t("lockedByEnv")} 🔒 {t("lockedByEnv")}
</span> </span>
)} )}
<Checkbox <Toggle
label=""
checked={on} checked={on}
disabled={locked} disabled={locked}
ariaLabel={t(labels.name)}
onChange={(checked) => onChange={(checked) =>
onChange(setExperimentalFlag(rawOther, flag.id, checked)) onChange(setExperimentalFlag(rawOther, flag.id, checked))
} }
@@ -160,17 +161,8 @@ export function SubagentSettingsPage({
</div> </div>
)} )}
{/* Subagent: experimental flag + secondary model in one card */} {/* Subagent: secondary model picker + inherited settings */}
<Card title={t("secondaryModelSection")}> <Card title={t("secondaryModelSection")}>
{renderFlagRow(secondaryFlag)}
<div className="border-t border-border" />
{!secondaryEnabled && (
<div className="bg-amber-100 dark:bg-amber-900/20 border border-amber-300 dark:border-amber-500/30 rounded-lg px-3 py-2 text-sm text-amber-800 dark:text-amber-400">
{t("secondaryModelDisabledHint")}
</div>
)}
<label className="flex items-center gap-3 text-sm text-content-primary"> <label className="flex items-center gap-3 text-sm text-content-primary">
<span className="text-content-muted">{t("secondaryModelLabel")}</span> <span className="text-content-muted">{t("secondaryModelLabel")}</span>
<select <select
@@ -200,21 +192,44 @@ export function SubagentSettingsPage({
</select> </select>
</label> </label>
<div className="border-t border-border" />
<div className="flex items-center gap-3">
<div className="flex-1 min-w-0">
<div className="text-xs text-content-muted">
{t(FLAG_LABELS[secondaryFlag.id].desc)}
<code className="ml-2">{secondaryFlag.envVar}</code>
</div>
</div>
{secondaryLocked && (
<span className="text-xs text-content-muted shrink-0">
🔒 {t("lockedByEnv")}
</span>
)}
<Toggle
checked={secondaryEnabled}
disabled={secondaryLocked}
ariaLabel={t(FLAG_LABELS[secondaryFlag.id].name)}
onChange={(checked) =>
onChange(setExperimentalFlag(rawOther, secondaryFlag.id, checked))
}
/>
</div>
{secondary.model !== undefined && ( {secondary.model !== undefined && (
<div className="rounded-lg border border-border bg-input/40 px-3 py-2 space-y-1.5"> <div className="rounded-lg border border-border bg-input/40 px-3 py-2.5 space-y-2">
<div className="flex items-center gap-3 text-sm"> <div className="flex items-baseline gap-2 text-sm">
<span className="text-content-muted w-24 shrink-0"> <span className="shrink-0 text-content-muted">
{t("secondaryModelInheritedContext")} {t("secondaryModelInheritedContext")}
</span> </span>
<span className="text-content-primary"> <span className="font-medium text-content-primary">
{inheritedContext ? fmtContext(inheritedContext) : "—"} {inheritedContext ? fmtContext(inheritedContext) : "—"}
</span> </span>
</div> </div>
<div className="flex items-center gap-3 text-sm"> <div className="flex items-baseline gap-2 text-sm">
<span className="text-content-muted w-24 shrink-0"> <span className="shrink-0 text-content-muted">
{t("secondaryModelInheritedEffort")} {t("secondaryModelInheritedEffort")}
</span> </span>
<span className="text-content-primary"> <span className="font-medium text-content-primary">
{inheritedEffort ? effortLabel(inheritedEffort) : "—"} {inheritedEffort ? effortLabel(inheritedEffort) : "—"}
</span> </span>
</div> </div>
@@ -224,7 +239,6 @@ export function SubagentSettingsPage({
</div> </div>
)} )}
<p className="text-xs text-content-muted">{t("secondaryModelHint")}</p>
<p className="text-xs text-content-muted">{t("secondaryModelPriorityHint")}</p> <p className="text-xs text-content-muted">{t("secondaryModelPriorityHint")}</p>
</Card> </Card>
+33
View File
@@ -42,6 +42,39 @@ export function Checkbox({
); );
} }
/** Pill-style toggle switch with a sliding knob. */
export function Toggle({
checked,
disabled,
onChange,
ariaLabel,
}: {
checked: boolean;
disabled?: boolean;
onChange: (checked: boolean) => void;
ariaLabel?: string;
}) {
return (
<button
type="button"
role="switch"
aria-checked={checked}
aria-label={ariaLabel}
disabled={disabled}
onClick={() => onChange(!checked)}
className={`relative h-5 w-9 shrink-0 rounded-full transition-colors duration-200 focus:ring-2 focus:ring-blue-500 focus:outline-none ${
checked ? "bg-blue-600" : "bg-border"
} ${disabled ? "cursor-not-allowed opacity-50" : ""}`}
>
<span
className={`absolute left-0.5 top-0.5 h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-200 ${
checked ? "translate-x-4" : "translate-x-0"
}`}
/>
</button>
);
}
export function NumberField({ export function NumberField({
label, label,
value, value,