diff --git a/apps/rowboat/app/lib/components/markdown-content.tsx b/apps/rowboat/app/lib/components/markdown-content.tsx index 724f36b2..16c7a73e 100644 --- a/apps/rowboat/app/lib/components/markdown-content.tsx +++ b/apps/rowboat/app/lib/components/markdown-content.tsx @@ -105,8 +105,6 @@ export default function MarkdownContent({ return ( {displayLabel} (!) diff --git a/apps/rowboat/app/lib/components/mentions_editor.tsx b/apps/rowboat/app/lib/components/mentions_editor.tsx index 35b0aef3..451a6f2b 100644 --- a/apps/rowboat/app/lib/components/mentions_editor.tsx +++ b/apps/rowboat/app/lib/components/mentions_editor.tsx @@ -29,7 +29,7 @@ Quill.register('modules/mention', Mention); function markdownToParts(markdown: string, atValues: Match[]): (string | Match)[] { // Regex match for pattern [@type:name](#type:something) where type is tool/prompt/agent - const mentionRegex = /\[@(tool|prompt|agent):([^\]]+)\]\(#mention\)/g; + const mentionRegex = /\[@(tool|prompt|agent|variable):([^\]]+)\]\(#mention\)/g; const parts: (string | Match)[] = []; let lastIndex = 0; diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index dcd4ce64..a1217f86 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -1254,9 +1254,27 @@ export function WorkflowEditor({ dispatch({ type: "update_pipeline", name, pipeline }); } - function handleDeleteAgent(name: string) { + async function handleDeleteAgent(name: string) { if (window.confirm(`Are you sure you want to delete the agent "${name}"?`)) { - dispatch({ type: "delete_agent", name }); + // Optimistically update UI (guard will show modal in live mode) + dispatchGuarded({ type: "delete_agent", name }); + // Persist immediately to avoid debounce races overwriting local state + if (!isLive) { + try { + const remainingAgents = state.present.workflow.agents.filter(a => a.name !== name); + const toSave = { + ...state.present.workflow, + agents: remainingAgents, + // If startAgent was deleted, set to first remaining or '' + startAgent: state.present.workflow.startAgent === name + ? (remainingAgents[0]?.name || '') + : state.present.workflow.startAgent, + } as z.infer; + await saveWorkflow(projectId, toSave); + } catch (e) { + console.error('Failed to persist agent deletion', e); + } + } } } @@ -1264,9 +1282,22 @@ export function WorkflowEditor({ dispatch({ type: "update_tool", name, tool }); } - function handleDeleteTool(name: string) { + async function handleDeleteTool(name: string) { if (window.confirm(`Are you sure you want to delete the tool "${name}"?`)) { - dispatch({ type: "delete_tool", name }); + // Optimistically update UI (guard will show modal in live mode) + dispatchGuarded({ type: "delete_tool", name }); + // Persist immediately to avoid debounce races that can re-add the tool + if (!isLive) { + try { + const toSave = { + ...state.present.workflow, + tools: state.present.workflow.tools.filter(t => t.name !== name), + } as z.infer; + await saveWorkflow(projectId, toSave); + } catch (e) { + console.error('Failed to persist tool deletion', e); + } + } } } @@ -1283,9 +1314,22 @@ export function WorkflowEditor({ dispatch({ type: "update_prompt_no_select", name, prompt }); } - function handleDeletePrompt(name: string) { + async function handleDeletePrompt(name: string) { if (window.confirm(`Are you sure you want to delete the prompt "${name}"?`)) { - dispatch({ type: "delete_prompt", name }); + // Optimistically update UI (guard will show modal in live mode) + dispatchGuarded({ type: "delete_prompt", name }); + // Persist immediately to avoid debounce races overwriting local state + if (!isLive) { + try { + const toSave = { + ...state.present.workflow, + prompts: state.present.workflow.prompts.filter(p => p.name !== name), + } as z.infer; + await saveWorkflow(projectId, toSave); + } catch (e) { + console.error('Failed to persist prompt deletion', e); + } + } } }