mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 13:29:44 +02:00
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:
@@ -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\"",
|
||||
|
||||
@@ -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 项目里收件箱加载慢的问题\"",
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user