fix(quick-create): move project picker into property pill row

Reviewer feedback: the picker felt out of place wedged next to the agent
header. Move it into a property toolbar row above the footer, reusing the
shared `ProjectPicker` + `PillButton` so its placement and styling line up
exactly with the manual create panel.

This also drops the bespoke dropdown / aria / label strings that were only
needed while the picker rendered inline beside "Created by".

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
Jiang Bohan
2026-05-09 15:45:42 +08:00
parent 1a7c0c33a8
commit 4c8d20221b
3 changed files with 19 additions and 74 deletions

View File

@@ -118,9 +118,6 @@
"select_agent_aria": "Select agent",
"pick_an_agent": "Pick an agent…",
"no_agents": "No agents available.",
"select_project_aria": "Select project",
"in_project": "in",
"no_project": "No project",
"version_missing": "This agent's daemon doesn't report a CLI version. Create with agent needs multica CLI ≥ {{min}}. Upgrade the daemon and reconnect, or switch to manual create.",
"version_below": "This agent's daemon CLI is {{current}} — Create with agent needs ≥ {{min}}. Upgrade the daemon, or switch to manual create.",
"prompt_placeholder": "Tell the agent what to do, e.g. \"let Bohan fix the inbox loading slowness in the Web project\"",

View File

@@ -118,9 +118,6 @@
"select_agent_aria": "选择智能体",
"pick_an_agent": "选一个智能体...",
"no_agents": "暂无可用智能体。",
"select_project_aria": "选择项目",
"in_project": "在",
"no_project": "无项目",
"version_missing": "该智能体的守护进程没有报告 CLI 版本。通过智能体创建需要 multica CLI ≥ {{min}}。请升级守护进程并重连,或切换到手动创建。",
"version_below": "该智能体的守护进程 CLI 是 {{current}}——通过智能体创建需要 ≥ {{min}}。请升级守护进程,或切换到手动创建。",
"prompt_placeholder": "告诉智能体要做什么,例如:\"让 Bohan 修一下 Web 项目里收件箱加载慢的问题\"",

View File

@@ -31,7 +31,8 @@ import { useFileUpload } from "@multica/core/hooks/use-file-upload";
import { formatShortcut, modKey, enterKey } from "@multica/core/platform";
import type { Agent } from "@multica/core/types";
import { ActorAvatar } from "../common/actor-avatar";
import { ProjectIcon } from "../projects/components/project-icon";
import { PillButton } from "../common/pill-button";
import { ProjectPicker } from "../projects/components/project-picker";
import { canAssignAgent } from "../issues/components/pickers/assignee-picker";
import { useAuthStore } from "@multica/core/auth";
import { memberListOptions } from "@multica/core/workspace/queries";
@@ -66,7 +67,6 @@ export function AgentCreatePanel({
data?: Record<string, unknown> | null;
}) {
const { t } = useT("modals");
const { t: tProjects } = useT("projects");
const workspaceName = useCurrentWorkspace()?.name;
const wsId = useWorkspaceId();
const userId = useAuthStore((s) => s.user?.id);
@@ -136,10 +136,6 @@ export function AgentCreatePanel({
if (projectId === null || projects.length === 0) return;
if (!projects.some((p) => p.id === projectId)) setProjectId(null);
}, [projects, projectId]);
const selectedProject = useMemo(
() => projects.find((p) => p.id === projectId),
[projects, projectId],
);
// Daemon CLI version gate. The agent-create flow needs the runtime's
// bundled multica CLI to be ≥ MIN_QUICK_CREATE_CLI_VERSION; older
@@ -312,12 +308,8 @@ export function AgentCreatePanel({
</button>
</div>
{/* Agent + project pickers — share one row so the modal stays compact.
The project picker remembers the user's last pick across opens
(useQuickCreateStore.lastProjectId) so workspaces that primarily
ship into one project don't have to retype "in project A" on
every prompt. */}
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 px-5 pt-1 pb-2 shrink-0">
{/* Agent picker */}
<div className="px-5 pt-1 pb-2 shrink-0">
<DropdownMenu>
<DropdownMenuTrigger
render={
@@ -371,62 +363,6 @@ export function AgentCreatePanel({
)}
</DropdownMenuContent>
</DropdownMenu>
{/* Project picker — visually subordinate to the agent picker (it's
the optional second target), placed on the same row so the
header stays one tight line. Hidden entirely when the workspace
has no projects — there is nothing to pick. */}
{projects.length > 0 && (
<DropdownMenu>
<DropdownMenuTrigger
render={
<button
type="button"
aria-label={t(($) => $.create_issue.agent.select_project_aria)}
className="flex max-w-[14rem] items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer rounded-sm px-1.5 py-1 hover:bg-accent/60"
>
{selectedProject ? (
<>
<span>{t(($) => $.create_issue.agent.in_project)}</span>
<span className="flex min-w-0 items-center gap-1.5 text-foreground">
<ProjectIcon project={selectedProject} size="sm" />
<span className="truncate">{selectedProject.title}</span>
</span>
</>
) : (
<span>{t(($) => $.create_issue.agent.no_project)}</span>
)}
</button>
}
/>
<DropdownMenuContent align="start" className="w-64 max-h-72 overflow-y-auto">
<DropdownMenuItem
onClick={() => setProjectId(null)}
className="flex items-center gap-2"
>
<span className="flex-1 truncate">
{tProjects(($) => $.picker.no_project)}
</span>
{projectId === null && (
<Check className="size-3.5 text-muted-foreground" />
)}
</DropdownMenuItem>
{projects.map((p) => (
<DropdownMenuItem
key={p.id}
onClick={() => setProjectId(p.id)}
className="flex items-center gap-2"
>
<ProjectIcon project={p} size="md" />
<span className="flex-1 truncate">{p.title}</span>
{projectId === p.id && (
<Check className="size-3.5 text-muted-foreground" />
)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
)}
</div>
{selectedAgent && versionBlocked && (
@@ -470,6 +406,21 @@ export function AgentCreatePanel({
<div className="px-5 pb-2 text-xs text-destructive">{error}</div>
)}
{/* Property toolbar — mirrors the manual panel's pill row so the
project pill sits in the same place across both modes. Agent mode
owns only the project (status / priority / assignee / due-date are
inferred from the prompt), so it's a single pill. The pick is
persisted per-workspace via useQuickCreateStore.lastProjectId so
users targeting one project skip retyping "in project X". */}
<div className="flex items-center gap-1.5 px-4 pb-2 shrink-0 flex-wrap">
<ProjectPicker
projectId={projectId}
onUpdate={(u) => setProjectId(u.project_id ?? null)}
triggerRender={<PillButton />}
align="start"
/>
</div>
{/* Footer */}
<div className="flex flex-col gap-2 border-t px-4 py-3 shrink-0 sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-h-7 items-center gap-2">