Skip to content

Commit f127b69

Browse files
feat: add centralized agent icon system and fix agent name formatting
1 parent 9546798 commit f127b69

File tree

6 files changed

+251
-215
lines changed

6 files changed

+251
-215
lines changed

src/frontend/src/components/content/HomeInput.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,20 @@ const truncateDescription = (description: string, maxLength: number = 180): stri
4040
return description;
4141
}
4242

43-
// Find the last space before the limit to avoid cutting words
43+
4444
const truncated = description.substring(0, maxLength);
4545
const lastSpaceIndex = truncated.lastIndexOf(' ');
4646

47-
// If there's a space within the last 20 characters, cut there
48-
// Otherwise, cut at the exact limit
4947
const cutPoint = lastSpaceIndex > maxLength - 20 ? lastSpaceIndex : maxLength;
5048

5149
return description.substring(0, cutPoint) + '...';
5250
};
5351

52+
// Extended QuickTask interface to store both truncated and full descriptions
53+
interface ExtendedQuickTask extends QuickTask {
54+
fullDescription: string; // Store the full, untruncated description
55+
}
56+
5457
const HomeInput: React.FC<HomeInputProps> = ({
5558
selectedTeam,
5659
}) => {
@@ -148,13 +151,12 @@ const HomeInput: React.FC<HomeInputProps> = ({
148151
}
149152
};
150153

151-
const handleQuickTaskClick = (task: QuickTask) => {
152-
setInput(task.description);
154+
const handleQuickTaskClick = (task: ExtendedQuickTask) => {
155+
setInput(task.fullDescription);
153156
setRAIError(null); // Clear any RAI errors when selecting a quick task
154157
if (textareaRef.current) {
155158
textareaRef.current.focus();
156159
}
157-
158160
};
159161

160162
useEffect(() => {
@@ -164,15 +166,16 @@ const HomeInput: React.FC<HomeInputProps> = ({
164166
}
165167
}, [input]);
166168

167-
// Convert team starting_tasks to QuickTask format
168-
const tasksToDisplay: QuickTask[] = selectedTeam && selectedTeam.starting_tasks ?
169+
// Convert team starting_tasks to ExtendedQuickTask format
170+
const tasksToDisplay: ExtendedQuickTask[] = selectedTeam && selectedTeam.starting_tasks ?
169171
selectedTeam.starting_tasks.map((task, index) => {
170172
// Handle both string tasks and StartingTask objects
171173
if (typeof task === 'string') {
172174
return {
173175
id: `team-task-${index}`,
174176
title: task,
175177
description: truncateDescription(task),
178+
fullDescription: task, // Store the full description
176179
icon: getIconFromString("📋")
177180
};
178181
} else {
@@ -183,6 +186,7 @@ const HomeInput: React.FC<HomeInputProps> = ({
183186
id: startingTask.id || `team-task-${index}`,
184187
title: startingTask.name || startingTask.prompt || 'Task',
185188
description: truncateDescription(taskDescription),
189+
fullDescription: taskDescription, // Store the full description
186190
icon: getIconFromString(startingTask.logo || "📋")
187191
};
188192
}

src/frontend/src/components/content/PlanPanelRight.tsx

Lines changed: 55 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,10 @@ import {
33
Body1,
44
} from "@fluentui/react-components";
55
import {
6-
PersonRegular,
76
ArrowTurnDownRightRegular,
87
} from "@fluentui/react-icons";
98
import { MPlanData, PlanDetailsProps } from "../../models";
10-
import { TaskService } from "../../services/TaskService";
11-
import { AgentTypeUtils, AgentType } from "../../models/enums";
9+
import { getAgentIcon, getAgentDisplayNameWithSuffix } from '../../utils/agentIconUtils';
1210
import ContentNotFound from "../NotFound/ContentNotFound";
1311

1412

@@ -18,121 +16,82 @@ const PlanPanelRight: React.FC<PlanDetailsProps> = ({
1816
planApprovalRequest
1917
}) => {
2018

21-
// Helper function to get clean agent display name
22-
const getAgentDisplayName = (agentName: string): string => {
23-
if (!agentName) return 'Assistant';
24-
25-
let cleanName = TaskService.cleanTextToSpaces(agentName);
26-
if (cleanName.toLowerCase().includes('agent')) {
27-
cleanName = cleanName.replace(/agent/gi, '').trim();
28-
}
29-
return cleanName.replace(/\b\w/g, l => l.toUpperCase()) || 'Assistant';
30-
};
31-
32-
// Helper function to get agent icon based on name and type
33-
const getAgentIcon = (agentName: string) => {
34-
// Try to determine agent type from name
35-
const cleanName = agentName.toLowerCase();
36-
let agentType: AgentType;
37-
38-
if (cleanName.includes('coder')) {
39-
agentType = AgentType.CODER;
40-
} else if (cleanName.includes('executor')) {
41-
agentType = AgentType.EXECUTOR;
42-
} else if (cleanName.includes('filesurfer')) {
43-
agentType = AgentType.FILE_SURFER;
44-
} else if (cleanName.includes('websurfer')) {
45-
agentType = AgentType.WEB_SURFER;
46-
} else if (cleanName.includes('hr')) {
47-
agentType = AgentType.HR;
48-
} else if (cleanName.includes('marketing')) {
49-
agentType = AgentType.MARKETING;
50-
} else if (cleanName.includes('procurement')) {
51-
agentType = AgentType.PROCUREMENT;
52-
} else if (cleanName.includes('proxy')) {
53-
agentType = AgentType.GENERIC;
54-
} else {
55-
agentType = AgentType.GENERIC;
56-
}
57-
58-
// Get the icon name from the utility
59-
const iconName = AgentTypeUtils.getAgentIcon(agentType);
60-
61-
// Return the appropriate icon component or fallback to PersonRegular
62-
return <PersonRegular style={{
63-
color: 'var(--colorPaletteBlueForeground2)',
64-
fontSize: '16px'
65-
}} />;
66-
};
67-
6819
if (!planData && !loading) {
6920
return <ContentNotFound subtitle="The requested page could not be found." />;
7021
}
7122

7223
if (!planApprovalRequest) {
73-
return null;
24+
return (
25+
<div style={{
26+
width: '280px',
27+
height: '100vh',
28+
padding: '20px',
29+
display: 'flex',
30+
alignItems: 'center',
31+
justifyContent: 'center',
32+
borderLeft: '1px solid var(--colorNeutralStroke1)',
33+
color: 'var(--colorNeutralForeground3)',
34+
fontSize: '14px',
35+
fontStyle: 'italic'
36+
}}>
37+
No plan available
38+
</div>
39+
);
7440
}
7541

76-
// Parse plan steps - items ending with colons are headings, others are substeps
77-
const parsePlanSteps = () => {
78-
if (!planApprovalRequest.steps || planApprovalRequest.steps.length === 0) return [];
79-
80-
const result: Array<{ type: 'heading' | 'substep'; text: string }> = [];
81-
82-
planApprovalRequest.steps.forEach(step => {
83-
const action = step.cleanAction || step.action || '';
84-
const trimmedAction = action.trim();
85-
86-
if (trimmedAction) {
87-
// Check if the step ends with a colon
88-
if (trimmedAction.endsWith(':')) {
89-
// This is a heading
90-
result.push({ type: 'heading', text: trimmedAction });
91-
} else {
92-
// This is a substep
93-
result.push({ type: 'substep', text: trimmedAction });
94-
}
95-
}
96-
});
42+
// Extract plan steps from the planApprovalRequest
43+
const extractPlanSteps = () => {
44+
if (!planApprovalRequest.steps || planApprovalRequest.steps.length === 0) {
45+
return [];
46+
}
9747

98-
return result;
48+
return planApprovalRequest.steps.map((step, index) => {
49+
const action = step.action || step.cleanAction || '';
50+
const isHeading = action.trim().endsWith(':');
51+
52+
return {
53+
text: action.trim(),
54+
isHeading,
55+
key: `${index}-${action.substring(0, 20)}`
56+
};
57+
}).filter(step => step.text.length > 0);
9958
};
10059

101-
// Render Plan Section with scrolling
60+
// Render Plan Section
10261
const renderPlanSection = () => {
103-
const parsedSteps = parsePlanSteps();
62+
const planSteps = extractPlanSteps();
10463

10564
return (
10665
<div style={{
66+
marginBottom: '24px',
10767
paddingBottom: '20px',
108-
marginBottom: '20px',
109-
borderBottom: '1px solid var(--colorNeutralStroke2)',
110-
maxHeight: '50vh',
111-
overflow: 'hidden',
112-
display: 'flex',
113-
flexDirection: 'column'
68+
borderBottom: '1px solid var(--colorNeutralStroke1)'
11469
}}>
11570
<Body1 style={{
11671
marginBottom: '16px',
117-
flexShrink: 0,
11872
fontSize: '14px',
11973
fontWeight: 600,
12074
color: 'var(--colorNeutralForeground1)'
12175
}}>
122-
Plan
76+
Plan Overview
12377
</Body1>
12478

125-
{/* Scrollable Plan Steps */}
126-
<div style={{
127-
flex: 1,
128-
overflow: 'auto',
129-
paddingRight: '8px'
130-
}}>
79+
{planSteps.length === 0 ? (
80+
<div style={{
81+
textAlign: 'center',
82+
color: 'var(--colorNeutralForeground3)',
83+
fontSize: '14px',
84+
fontStyle: 'italic',
85+
padding: '20px'
86+
}}>
87+
Plan is being generated...
88+
</div>
89+
) : (
13190
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
132-
{parsedSteps.map((step, index) => (
133-
<div key={index}>
134-
{step.type === 'heading' ? (
135-
// Heading - no arrow, just the text
91+
{planSteps.map((step, index) => (
92+
<div key={step.key} style={{ display: 'flex', flexDirection: 'column' }}>
93+
{step.isHeading ? (
94+
// Heading - larger text, bold
13695
<Body1 style={{
13796
fontSize: '14px',
13897
fontWeight: 600,
@@ -167,7 +126,7 @@ const PlanPanelRight: React.FC<PlanDetailsProps> = ({
167126
</div>
168127
))}
169128
</div>
170-
</div>
129+
)}
171130
</div>
172131
);
173132
};
@@ -220,7 +179,7 @@ const PlanPanelRight: React.FC<PlanDetailsProps> = ({
220179
justifyContent: 'center',
221180
flexShrink: 0
222181
}}>
223-
{getAgentIcon(agentName)}
182+
{getAgentIcon(agentName, planData, planApprovalRequest)}
224183
</div>
225184

226185
{/* Agent Info - just name */}
@@ -230,7 +189,7 @@ const PlanPanelRight: React.FC<PlanDetailsProps> = ({
230189
fontSize: '14px',
231190
color: 'var(--colorNeutralForeground1)'
232191
}}>
233-
{getAgentDisplayName(agentName)} Agent
192+
{getAgentDisplayNameWithSuffix(agentName)}
234193
</Body1>
235194
</div>
236195
</div>

0 commit comments

Comments
 (0)