akseljoonas HF Staff commited on
Commit
b1370a3
Β·
1 Parent(s): 3b81a9c

feat: input/output toggle in CodePanel, tool error handling

Browse files
frontend/src/components/Chat/ToolCallGroup.tsx CHANGED
@@ -387,17 +387,22 @@ export default function ToolCallGroup({ tools, approveTools }: ToolCallGroupProp
387
  return;
388
  }
389
 
 
 
390
  if ((tool.state === 'output-available' || tool.state === 'output-error') && tool.output) {
391
  let language = 'text';
392
  const content = String(tool.output);
393
  if (content.trim().startsWith('{') || content.trim().startsWith('[')) language = 'json';
394
  else if (content.includes('```')) language = 'markdown';
395
 
396
- setPanel({ title: displayName, output: { content, language } }, 'output');
 
 
 
 
397
  setRightPanelOpen(true);
398
  } else if (args) {
399
- const content = JSON.stringify(args, null, 2);
400
- setPanel({ title: displayName, output: { content, language: 'json' } }, 'output');
401
  setRightPanelOpen(true);
402
  }
403
  },
 
387
  return;
388
  }
389
 
390
+ const inputSection = args ? { content: JSON.stringify(args, null, 2), language: 'json' } : undefined;
391
+
392
  if ((tool.state === 'output-available' || tool.state === 'output-error') && tool.output) {
393
  let language = 'text';
394
  const content = String(tool.output);
395
  if (content.trim().startsWith('{') || content.trim().startsWith('[')) language = 'json';
396
  else if (content.includes('```')) language = 'markdown';
397
 
398
+ setPanel({ title: displayName, output: { content, language }, input: inputSection }, 'output');
399
+ setRightPanelOpen(true);
400
+ } else if (tool.state === 'output-error') {
401
+ const content = `Tool \`${tool.toolName}\` returned an error with no output message.`;
402
+ setPanel({ title: displayName, output: { content, language: 'markdown' }, input: inputSection }, 'output');
403
  setRightPanelOpen(true);
404
  } else if (args) {
405
+ setPanel({ title: displayName, output: { content: JSON.stringify(args, null, 2), language: 'json' }, input: inputSection }, 'output');
 
406
  setRightPanelOpen(true);
407
  }
408
  },
frontend/src/components/CodePanel/CodePanel.tsx CHANGED
@@ -137,6 +137,7 @@ export default function CodePanel() {
137
  const [editedContent, setEditedContent] = useState('');
138
  const [originalContent, setOriginalContent] = useState('');
139
  const [copied, setCopied] = useState(false);
 
140
 
141
  const isDark = themeMode === 'dark';
142
  const syntaxTheme = isDark ? vscDarkPlus : vs;
@@ -149,6 +150,11 @@ export default function CodePanel() {
149
  const isEditableScript = panelView === 'script' && panelEditable;
150
  const hasUnsavedChanges = isEditing && editedContent !== originalContent;
151
 
 
 
 
 
 
152
  // Sync edited content when panel data changes
153
  useEffect(() => {
154
  if (panelData?.script?.content && panelView === 'script' && panelEditable) {
@@ -205,13 +211,15 @@ export default function CodePanel() {
205
  }
206
  }, [isEditing, editedContent, activeSection?.content]);
207
 
 
 
208
  const displayContent = useMemo(() => {
209
- if (!activeSection?.content) return '';
210
- if (!activeSection.language || activeSection.language === 'text') {
211
- return processLogs(activeSection.content);
212
  }
213
- return activeSection.content;
214
- }, [activeSection?.content, activeSection?.language]);
215
 
216
  useEffect(() => {
217
  if (scrollRef.current && panelView === 'output') {
@@ -240,7 +248,7 @@ export default function CodePanel() {
240
 
241
  // ── Content renderer ───────────────────────────────────────────
242
  const renderContent = () => {
243
- if (!activeSection?.content) {
244
  return (
245
  <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', opacity: 0.5 }}>
246
  <Typography variant="caption">NO CONTENT TO DISPLAY</Typography>
@@ -248,7 +256,7 @@ export default function CodePanel() {
248
  );
249
  }
250
 
251
- if (isEditing && isEditableScript) {
252
  return (
253
  <Box sx={{ position: 'relative', width: '100%', height: '100%' }}>
254
  <SyntaxHighlighter
@@ -295,7 +303,7 @@ export default function CodePanel() {
295
  );
296
  }
297
 
298
- const lang = activeSection.language;
299
  if (lang === 'python') return renderSyntaxBlock('python');
300
  if (lang === 'json') return renderSyntaxBlock('json');
301
 
@@ -480,6 +488,34 @@ export default function CodePanel() {
480
  overflow: 'auto',
481
  }}
482
  >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
483
  {renderContent()}
484
  </Box>
485
  </Box>
 
137
  const [editedContent, setEditedContent] = useState('');
138
  const [originalContent, setOriginalContent] = useState('');
139
  const [copied, setCopied] = useState(false);
140
+ const [showInput, setShowInput] = useState(false);
141
 
142
  const isDark = themeMode === 'dark';
143
  const syntaxTheme = isDark ? vscDarkPlus : vs;
 
150
  const isEditableScript = panelView === 'script' && panelEditable;
151
  const hasUnsavedChanges = isEditing && editedContent !== originalContent;
152
 
153
+ // Reset input toggle when panel data changes
154
+ useEffect(() => {
155
+ setShowInput(false);
156
+ }, [panelData]);
157
+
158
  // Sync edited content when panel data changes
159
  useEffect(() => {
160
  if (panelData?.script?.content && panelView === 'script' && panelEditable) {
 
211
  }
212
  }, [isEditing, editedContent, activeSection?.content]);
213
 
214
+ const visibleSection = (showInput && panelData?.input) ? panelData.input : activeSection;
215
+
216
  const displayContent = useMemo(() => {
217
+ if (!visibleSection?.content) return '';
218
+ if (!visibleSection.language || visibleSection.language === 'text') {
219
+ return processLogs(visibleSection.content);
220
  }
221
+ return visibleSection.content;
222
+ }, [visibleSection?.content, visibleSection?.language]);
223
 
224
  useEffect(() => {
225
  if (scrollRef.current && panelView === 'output') {
 
248
 
249
  // ── Content renderer ───────────────────────────────────────────
250
  const renderContent = () => {
251
+ if (!visibleSection?.content) {
252
  return (
253
  <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', opacity: 0.5 }}>
254
  <Typography variant="caption">NO CONTENT TO DISPLAY</Typography>
 
256
  );
257
  }
258
 
259
+ if (!showInput && isEditing && isEditableScript) {
260
  return (
261
  <Box sx={{ position: 'relative', width: '100%', height: '100%' }}>
262
  <SyntaxHighlighter
 
303
  );
304
  }
305
 
306
+ const lang = visibleSection.language;
307
  if (lang === 'python') return renderSyntaxBlock('python');
308
  if (lang === 'json') return renderSyntaxBlock('json');
309
 
 
488
  overflow: 'auto',
489
  }}
490
  >
491
+ {/* Input / Output toggle */}
492
+ {panelData?.input && panelView === 'output' && (
493
+ <Box sx={{ display: 'flex', gap: 0.5, mb: 1.5 }}>
494
+ {['output', 'input'].map((tab) => (
495
+ <Typography
496
+ key={tab}
497
+ onClick={() => setShowInput(tab === 'input')}
498
+ variant="caption"
499
+ sx={{
500
+ fontSize: '0.65rem',
501
+ fontWeight: 600,
502
+ textTransform: 'uppercase',
503
+ letterSpacing: '0.05em',
504
+ cursor: 'pointer',
505
+ px: 1,
506
+ py: 0.25,
507
+ borderRadius: 0.5,
508
+ color: (tab === 'input') === showInput ? 'var(--text)' : 'var(--muted-text)',
509
+ bgcolor: (tab === 'input') === showInput ? 'var(--hover-bg)' : 'transparent',
510
+ transition: 'all 0.12s ease',
511
+ '&:hover': { color: 'var(--text)' },
512
+ }}
513
+ >
514
+ {tab}
515
+ </Typography>
516
+ ))}
517
+ </Box>
518
+ )}
519
  {renderContent()}
520
  </Box>
521
  </Box>
frontend/src/store/agentStore.ts CHANGED
@@ -27,6 +27,7 @@ export interface PanelData {
27
  title: string;
28
  script?: PanelSection;
29
  output?: PanelSection;
 
30
  parameters?: Record<string, unknown>;
31
  }
32
 
 
27
  title: string;
28
  script?: PanelSection;
29
  output?: PanelSection;
30
+ input?: PanelSection;
31
  parameters?: Record<string, unknown>;
32
  }
33