diff --git a/backend/apps/workflows/workflows.py b/backend/apps/workflows/workflows.py index b87eef12..46f6494c 100644 --- a/backend/apps/workflows/workflows.py +++ b/backend/apps/workflows/workflows.py @@ -597,11 +597,21 @@ async def edit_agent_session(workflow_id: str): from backend.apps.agents.core.models import AgentConfig from backend.apps.agents.agent_manager import agent_manager steps_lines = "\n".join(f"{i+1}. {(s.label or '').strip() or (s.text or '')[:60]}\n Prompt: {s.text}" for i, s in enumerate(wf.steps)) + # A brand-new workflow ("+ New" in the hub) opens here with zero steps, so + # frame the agent as a builder rather than a fix-what-exists editor. + intro = ( + "Help the user iterate on it." + if wf.steps + else "This workflow is brand new and has no steps yet. Help the user " + "build it from scratch: ask what it should do, then add steps with " + "AddWorkflowStep." + ) + steps_block = f"Current steps:\n{steps_lines}\n\n" if wf.steps else "It has no steps yet.\n\n" system_prompt = ( f"You are the Edit Agent for the user's saved workflow \"{wf.title}\" " - f"(id: {wf.id}). Help the user iterate on it. The workflow's purpose: " + f"(id: {wf.id}). {intro} The workflow's purpose: " f"{wf.description or '(unspecified)'}.\n\n" - f"Current steps:\n{steps_lines}\n\n" + f"{steps_block}" "How to work:\n" "1. When the user describes a change, briefly confirm what you'll do.\n" "2. If you need to look at files / search / activate an MCP / etc. to " diff --git a/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx b/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx index b6a5a30d..8b163341 100644 --- a/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx +++ b/frontend/src/app/pages/Dashboard/DashboardToolbar.tsx @@ -343,11 +343,7 @@ const DashboardToolbar = React.forwardRef( if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) return; const el = target instanceof Element ? target : (target as Node).parentElement; - // The New Chat / Schedule pill row renders OUTSIDE containerRef, so a - // pill click counts as "outside" and used to fire handleDismiss on - // mouseup. That closed the composer (inputOpen=false), which unmounted - // the pill row before its click landed, so Schedule's onClick never ran - // ("Schedule does nothing"). Exempt the pills; they own their own onClick. + if (el?.closest('[role="dialog"], [role="presentation"], .MuiModal-root, .MuiPopover-root, [data-toolbar-pills]')) { return; } @@ -420,9 +416,6 @@ const DashboardToolbar = React.forwardRef( return ( <> {(inputOpen || historyOpen) && ( - // Image #54: paired mode pills above the composer/popover. - // The two states are mutually exclusive: opening one closes the - // other so the body underneath only renders one thing at a time. { diff --git a/frontend/src/shared/state/dashboardLayoutSlice.ts b/frontend/src/shared/state/dashboardLayoutSlice.ts index f31b43ac..7068096f 100644 --- a/frontend/src/shared/state/dashboardLayoutSlice.ts +++ b/frontend/src/shared/state/dashboardLayoutSlice.ts @@ -27,7 +27,7 @@ export const GRID_GAP = 24; const GRID_ORIGIN = { x: 40, y: 100 }; const GRID_COLS_FALLBACK = 4; -export type CardType = 'agent' | 'view' | 'browser' | 'note' | 'workflow'; +export type CardType = 'agent' | 'view' | 'browser' | 'note' | 'workflow' | 'workflows-hub'; export interface CardPosition { session_id: string; @@ -1080,7 +1080,7 @@ const dashboardLayoutSlice = createSlice({ moveCards( state, action: PayloadAction<{ - items: Array<{ id: string; type: 'agent' | 'view' | 'browser' | 'note' | 'workflow' }>; + items: Array<{ id: string; type: CardType }>; dx: number; dy: number; }>, @@ -1111,6 +1111,11 @@ const dashboardLayoutSlice = createSlice({ card.x += dx; card.y += dy; } + } else if (item.type === 'workflows-hub') { + if (state.workflowsHub) { + state.workflowsHub.x += dx; + state.workflowsHub.y += dy; + } } else { const card = state.browserCards[item.id]; if (card) {