fix(agent): Agent 模型选择 - 复用创造页选择器并只展示文本模型
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -5,6 +5,12 @@ description: 当前项目已实现功能的手工验证清单与未确认边界
|
||||
|
||||
# 待测试
|
||||
|
||||
## Agent 对话模型选择器复用创造页样式
|
||||
|
||||
- Agent 底部 Composer 与设置页「对话模型」改为复用画布节点 / 创造页的 `ModelPicker` `variant="creation"`:先选品牌、再选模型,不再把品牌挤成一行。
|
||||
- 目录只展示文本模型;图片、视频、音频模型不进入 Agent 对话选择。当前选中若不是文本模型,回退到第一个可用文本模型。
|
||||
- 待登录后打开画布 Agent:核对品牌列表为纵向卡片、点入后出现双栏模型列表且高度跟品牌列表走、不再被 300px 最小高度撑空;设置页同样;深浅主题与窄面板不裁切。
|
||||
|
||||
## 设置页页头与生成偏好精简
|
||||
|
||||
- 设置页去掉「创作偏好 / 配置默认模型、生成参数和素材存储。」页头;仅在 `?continue=1` 时保留返回创作按钮。
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { AgentMediaSettings } from "@/services/api/agent";
|
||||
import { CanvasAgentImageApprovalSettings } from "./canvas-agent-image-approval-settings";
|
||||
import { addSkill, listAddedSkills, listSkills, type Skill, type SkillCategory } from "@/services/api/skills";
|
||||
import { clearCloudAgentPendingSubmission, cloudAgentConversationTitle, loadCloudAgentConversations, loadCloudAgentPendingSubmission, saveCloudAgentConversations, saveCloudAgentPendingSubmission, type CloudAgentConversation, type CloudAgentPendingSubmission } from "@/services/cloud-agent-conversations";
|
||||
import { logicalModelIDForConfig, modelOptionName, resolveModelRequestConfig, useConfigStore, useEffectiveConfig } from "@/stores/use-config-store";
|
||||
import { logicalModelIDForConfig, modelOptionName, resolveModelRequestConfig, selectableModelsByCapability, useConfigStore, useEffectiveConfig } from "@/stores/use-config-store";
|
||||
import { useActiveTheme } from "@/stores/canvas/use-canvas-theme-store";
|
||||
import { applyAgentCanvasPatches, refreshCanvasAfterAgent, saveRemoteUserDataNow } from "@/services/user-data-sync";
|
||||
import { createAgentCanvasSync } from "@/services/agent-canvas-sync";
|
||||
@@ -99,7 +99,11 @@ export function CanvasCloudAgentPanel({ canvasId, domainProjectId, nodeCount, re
|
||||
const currentScope = useRef(conversationScope);
|
||||
currentScope.current = conversationScope;
|
||||
const running = Boolean(run?.cleanupPending) || run?.status === "running" || run?.status === "queued" || run?.status === "waiting_approval";
|
||||
const selectedModel = config.textModel || config.model || "";
|
||||
const selectedModel = useMemo(() => {
|
||||
const textModels = selectableModelsByCapability(config, "text");
|
||||
const preferred = config.textModel || config.model || "";
|
||||
return textModels.includes(preferred) ? preferred : (textModels[0] || "");
|
||||
}, [config]);
|
||||
const reasoningSupported = Boolean(modelCapabilityConfigFor(config, selectedModel).text?.thinking);
|
||||
useEffect(() => { if (!reasoningSupported && reasoningMode !== "off") setReasoningMode("off"); }, [reasoningSupported, reasoningMode]);
|
||||
const installedSkills = useMemo(() => skills.filter((skill) => skill.isAdded), [skills]);
|
||||
@@ -946,11 +950,12 @@ function ComposerControls({
|
||||
value={selectedModel}
|
||||
capability="text"
|
||||
onChange={onModelChange}
|
||||
variant="creation"
|
||||
className="!h-8 !min-w-0 !w-36 !max-w-full !border-0 !bg-transparent !px-1.5 !shadow-none"
|
||||
popoverClassName="agent-model-picker-popover"
|
||||
showSelectedPrice={false}
|
||||
showOptionPrices
|
||||
placeholder="选择模型"
|
||||
placeholder="选择文本模型"
|
||||
/>
|
||||
{reasoningSupported ? <Dropdown trigger={["click"]} placement="topLeft" menu={{ items: reasoningMenuItems(reasoningMode, onReasoningModeChange) }}>
|
||||
<button type="button" aria-label="选择 Agent 推理模式" title="推理模式:只用于规划和工具选择" className="flex h-8 shrink-0 items-center gap-1 rounded-md px-2 text-[11px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-current/25" style={{ color: reasoningMode === "off" ? theme.node.muted : theme.accent.primary, background: reasoningMode === "off" ? "transparent" : theme.node.fill }}>
|
||||
|
||||
@@ -102,7 +102,7 @@ function SettingsHome({ props, theme, onOpen }: { props: AgentSettingsProps; the
|
||||
<section>
|
||||
<SettingLabel label="对话模型" hint="新运行生效" />
|
||||
<div className="min-w-0 rounded-xl px-2 py-1.5" style={{ background: theme.node.fill }}>
|
||||
<ModelPicker config={props.config} value={props.selectedModel} capability="text" onChange={props.onModelChange} fullWidth showSelectedPrice showOptionPrices placeholder="选择 Agent 模型" className="!border-0 !bg-transparent !shadow-none" />
|
||||
<ModelPicker config={props.config} value={props.selectedModel} capability="text" onChange={props.onModelChange} variant="creation" fullWidth showSelectedPrice showOptionPrices placeholder="选择文本模型" className="!border-0 !bg-transparent !shadow-none" popoverClassName="agent-model-picker-popover" />
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
/* Agent 浮窗高于普通画布层,模型菜单必须叠在对话窗之上。 */
|
||||
.agent-model-picker-popover {
|
||||
z-index: calc(var(--z-modal-overlay) + 1000) !important;
|
||||
}
|
||||
|
||||
/* Flex 子页必须允许收缩,长模型名和 Skill 文案不能决定窗口宽度。 */
|
||||
.canvas-agent-panel > div,
|
||||
.canvas-agent-settings,
|
||||
|
||||
@@ -563,7 +563,7 @@
|
||||
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-brand-list { width: min(360px, calc(100vw - 24px)) !important; }
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-model-list { width: min(720px, calc(100vw - 24px)) !important; }
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-model-list .canvas-model-picker-two-pane { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 14px; min-height: 300px; }
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-model-list .canvas-model-picker-two-pane { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 14px; min-height: 0; align-items: start; }
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-model-list .canvas-model-picker-brand-rail { display: grid; align-content: start; gap: 5px; border-right: 1px solid var(--workspace-border); padding-right: 14px; }
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-model-list .canvas-model-picker-brand { width: 100%; }
|
||||
.creation-model-picker-surface .creation-model-picker-menu.is-model-list .canvas-model-picker-brand.is-active { border-color: var(--workspace-border); background: var(--workspace-surface-hover); }
|
||||
|
||||
27
web/test/agent-model-picker.test.ts
Normal file
27
web/test/agent-model-picker.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { expect, test } from "bun:test";
|
||||
|
||||
test("Agent 对话和设置复用创作页模型选择器,并且只展示文本模型", async () => {
|
||||
const panel = await Bun.file(new URL("../src/components/canvas/canvas-cloud-agent-panel.tsx", import.meta.url)).text();
|
||||
const settings = await Bun.file(new URL("../src/components/canvas/canvas-cloud-agent-settings.tsx", import.meta.url)).text();
|
||||
const css = await Bun.file(new URL("../src/components/canvas/canvas-cloud-agent.css", import.meta.url)).text();
|
||||
const pickerCss = await Bun.file(new URL("../src/styles/workspace-product.css", import.meta.url)).text();
|
||||
|
||||
expect(panel).toContain('capability="text"');
|
||||
expect(panel).toContain('variant="creation"');
|
||||
expect(panel).toContain('popoverClassName="agent-model-picker-popover"');
|
||||
expect(panel).toContain('selectableModelsByCapability(config, "text")');
|
||||
expect(panel).toContain('placeholder="选择文本模型"');
|
||||
|
||||
expect(settings).toContain('capability="text"');
|
||||
expect(settings).toContain('variant="creation"');
|
||||
expect(settings).toContain('popoverClassName="agent-model-picker-popover"');
|
||||
expect(settings).toContain('placeholder="选择文本模型"');
|
||||
|
||||
expect(css).toContain(".agent-model-picker-popover");
|
||||
expect(css).toContain("z-index: calc(var(--z-modal-overlay) + 1000)");
|
||||
|
||||
const twoPane = pickerCss.match(/\.creation-model-picker-menu\.is-model-list \.canvas-model-picker-two-pane \{[^}]+\}/)?.[0] || "";
|
||||
expect(twoPane).toContain("min-height: 0");
|
||||
expect(twoPane).toContain("align-items: start");
|
||||
expect(twoPane).not.toContain("min-height: 300px");
|
||||
});
|
||||
Reference in New Issue
Block a user