diff --git a/apps/rowboat/app/lib/components/atmentions.ts b/apps/rowboat/app/lib/components/atmentions.ts index 8a17a39d..0d52adf1 100644 --- a/apps/rowboat/app/lib/components/atmentions.ts +++ b/apps/rowboat/app/lib/components/atmentions.ts @@ -23,6 +23,7 @@ export function createAtMentions({ agents, prompts, tools, currentAgentName }: C atMentions.push({ id, value: id, + label: `Agent: ${a.name}`, denotationChar: "@", // Add required properties for Match type link: id, target: "_self" @@ -35,6 +36,7 @@ export function createAtMentions({ agents, prompts, tools, currentAgentName }: C atMentions.push({ id, value: id, + label: `Prompt: ${prompt.name}`, denotationChar: "@", link: id, target: "_self" @@ -47,6 +49,7 @@ export function createAtMentions({ agents, prompts, tools, currentAgentName }: C atMentions.push({ id, value: id, + label: `Tool: ${tool.name}`, denotationChar: "@", link: id, target: "_self" diff --git a/apps/rowboat/app/lib/components/editable-field.tsx b/apps/rowboat/app/lib/components/editable-field.tsx index 9a91e5e3..2f12c584 100644 --- a/apps/rowboat/app/lib/components/editable-field.tsx +++ b/apps/rowboat/app/lib/components/editable-field.tsx @@ -7,6 +7,7 @@ import { Label } from "./label"; import dynamic from "next/dynamic"; import { Match } from "./mentions_editor"; import { SparklesIcon } from "lucide-react"; +import { useEntitySelection } from "../../projects/[projectId]/workflow/workflow_editor"; const MentionsEditor = dynamic(() => import('./mentions_editor'), { ssr: false }); interface EditableFieldProps { @@ -30,6 +31,7 @@ interface EditableFieldProps { show: boolean; setShow: (show: boolean) => void; }; + onMentionNavigate?: (type: 'agent' | 'tool' | 'prompt', name: string) => void; } export function EditableField({ @@ -50,6 +52,7 @@ export function EditableField({ error, inline = false, showGenerateButton, + onMentionNavigate, }: EditableFieldProps) { const [isEditing, setIsEditing] = useState(false); const [localValue, setLocalValue] = useState(value); @@ -73,6 +76,18 @@ export function EditableField({ setIsEditing(false); }); + let contextMentionNavigate: { onSelectAgent: (name: string) => void; onSelectTool: (name: string) => void; onSelectPrompt: (name: string) => void; } | undefined; + try { + contextMentionNavigate = useEntitySelection(); + } catch {} + const handleMentionNavigate = onMentionNavigate || ((type, name) => { + if (contextMentionNavigate) { + if (type === 'agent') contextMentionNavigate.onSelectAgent(name); + else if (type === 'tool') contextMentionNavigate.onSelectTool(name); + else if (type === 'prompt') contextMentionNavigate.onSelectPrompt(name); + } + }); + const commonProps = { autoFocus: true, value: localValue, @@ -236,10 +251,10 @@ export function EditableField({ {value ? ( <> {markdown &&
- +
} {!markdown &&
- +
} ) : ( diff --git a/apps/rowboat/app/lib/components/markdown-content.tsx b/apps/rowboat/app/lib/components/markdown-content.tsx index ad4c54ac..724f36b2 100644 --- a/apps/rowboat/app/lib/components/markdown-content.tsx +++ b/apps/rowboat/app/lib/components/markdown-content.tsx @@ -4,10 +4,12 @@ import { Match } from './mentions_editor'; export default function MarkdownContent({ content, - atValues = [] + atValues = [], + onMentionNavigate, }: { content: string; atValues?: Match[]; + onMentionNavigate?: (type: 'agent' | 'tool' | 'prompt', name: string) => void; }) { return
atValue.id === label); + const handleMentionClick = (e: React.MouseEvent) => { + if (onMentionNavigate && type && name) { + e.preventDefault(); + onMentionNavigate(type, name); + } + }; if (atValues.length > 0 && invalid) { return ( - - @{label} (!) + + {displayLabel} (!) ); } return ( - - @{label} + + {displayLabel} ); } diff --git a/apps/rowboat/app/lib/components/mentions_editor.tsx b/apps/rowboat/app/lib/components/mentions_editor.tsx index 54c80f53..e7eb3fd8 100644 --- a/apps/rowboat/app/lib/components/mentions_editor.tsx +++ b/apps/rowboat/app/lib/components/mentions_editor.tsx @@ -11,6 +11,7 @@ export type Match = { id: string; value: string; invalid?: boolean; + label?: string; [key: string]: string | boolean | undefined; }; @@ -18,7 +19,7 @@ class CustomMentionBlot extends MentionBlot { static render(data: any) { const element = document.createElement('span'); element.className = data.invalid ? 'invalid' : ''; - element.textContent = data.invalid ? `${data.value} (!)` : data.value; + element.textContent = data.invalid ? `${data.label || data.value} (!)` : (data.label || data.value); return element; } } @@ -154,7 +155,7 @@ export default function MentionEditor({ renderItem: (item: Match) => { const div = document.createElement('div'); div.className = "px-2 py-1 bg-white text-blue-800 hover:bg-blue-100 cursor-pointer"; - div.textContent = item.id; + div.textContent = item.label || item.id; return div; }, } diff --git a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx index 897f20e4..ea817764 100644 --- a/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx +++ b/apps/rowboat/app/projects/[projectId]/workflow/workflow_editor.tsx @@ -553,6 +553,19 @@ function reducer(state: State, action: Action): State { return newState; } +// Context for entity selection +export const EntitySelectionContext = createContext<{ + onSelectAgent: (name: string) => void; + onSelectTool: (name: string) => void; + onSelectPrompt: (name: string) => void; +} | null>(null); + +export function useEntitySelection() { + const ctx = useContext(EntitySelectionContext); + if (!ctx) throw new Error('useEntitySelection must be used within EntitySelectionContext'); + return ctx; +} + export function WorkflowEditor({ dataSources, workflow, @@ -819,276 +832,284 @@ export function WorkflowEditor({ setIsInitialState(false); } - return
-
-
- - -
- -
-
-
- {state.present.publishing && } - {isLive &&
- - Live -
} - {!isLive &&
- - Draft -
} - {/* Download JSON icon button, with tooltip, to the left of the menu */} - - - - - + return ( + +
+
+
+ +
- - - +
- - { - if (key === 'switch') { - handleShowSelector(); - } - if (key === 'clone') { - handleCloneVersion(state.present.workflow._id); - } - }} - > -
} - className="gap-x-2" - > - View versions - + +
+ {state.present.publishing && } + {isLive &&
+ + Live +
} + {!isLive &&
+ + Draft +
} + {/* Download JSON icon button, with tooltip, to the left of the menu */} + + + + + +
+ + + +
+
+ { + if (key === 'switch') { + handleShowSelector(); + } + if (key === 'clone') { + handleCloneVersion(state.present.workflow._id); + } + }} + > +
} + className="gap-x-2" + > + View versions + -
} - className="gap-x-2" +
} + className="gap-x-2" + > + Clone this version + + +
+
+
+ {showCopySuccess &&
+
Copied to clipboard
+
} +
+ {isLive &&
+
+ + This version is locked. You cannot make changes. Changes applied through copilot willnotbe reflected. +
+
-
- {showCopySuccess &&
-
Copied to clipboard
-
} -
- {isLive &&
-
- - This version is locked. You cannot make changes. Changes applied through copilot willnotbe reflected. + + +
} + {!isLive && <> + + + + + }
- - -
} - {!isLive && <> - - - - - } -
-
- - -
-
-
- - - - {showCopilot && ( - <> + + +
+ +
+
setCopilotWidth(size)} + minSize={20} + defaultSize={showCopilot ? PANEL_RATIOS.chatApp : PANEL_RATIOS.chatApp + PANEL_RATIOS.copilot} + className="overflow-auto" > - - - )} -
- {USE_PRODUCT_TOUR && showTour && ( - setShowTour(false)} - /> - )} -
; + {showCopilot && ( + <> + + setCopilotWidth(size)} + > + 0 ? { + type: 'chat', + messages: chatMessages + } : undefined + } + isInitialState={isInitialState} + dataSources={dataSources} + /> + + + )} + + {USE_PRODUCT_TOUR && showTour && ( + setShowTour(false)} + /> + )} + + + ); }