Skip to main content
Glama
StepResultTab.tsx8.48 kB
import { Button } from '@/src/components/ui/button'; import { Tabs, TabsList, TabsTrigger } from '@/src/components/ui/tabs'; import { downloadJson } from '@/src/lib/download-utils'; import { isEmptyData } from '@/src/lib/general-utils'; import { Download, Loader2, OctagonX, X } from 'lucide-react'; import { useState } from 'react'; import { JsonCodeEditor } from '../../../editors/JsonCodeEditor'; import { useDataProcessor } from '../../hooks/use-data-processor'; import { CopyButton } from '../../shared/CopyButton'; import { isAbortError } from '@/src/lib/general-utils'; interface StepResultTabProps { step: any; stepIndex: number; stepResult?: any; failedSteps?: string[]; abortedSteps?: string[]; isExecuting?: boolean; isGlobalExecuting?: boolean; currentExecutingStepIndex?: number; isActive?: boolean; } export function StepResultTab({ step, stepIndex, stepResult, failedSteps = [], abortedSteps = [], isExecuting, isGlobalExecuting, currentExecutingStepIndex, isActive = true, }: StepResultTabProps) { const [outputViewMode, setOutputViewMode] = useState<'preview' | 'schema'>('preview'); const outputProcessor = useDataProcessor(stepResult, isActive); const handleOutputViewModeChange = (mode: 'preview' | 'schema') => { setOutputViewMode(mode); if (mode === 'schema') { outputProcessor.computeSchema(); } }; const stepFailed = failedSteps?.includes(step.id); const stepAborted = abortedSteps?.includes(step.id); const errorResult = (stepFailed || stepAborted) && (!stepResult || typeof stepResult === 'string'); const isPending = !stepFailed && !stepAborted && stepResult === undefined; const isActivelyRunning = !!(isExecuting || (isGlobalExecuting && currentExecutingStepIndex === stepIndex)); let outputString = ''; let isTruncated = false; if (!isPending) { if (errorResult) { if (stepResult) { outputString = stepResult.length > 50000 ? stepResult.substring(0, 50000) + '\n... [Error message truncated]' : stepResult; } else { outputString = '{\n "error": "Step execution failed"\n}'; } } else { outputString = outputViewMode === 'schema' ? outputProcessor.schema?.displayString || '' : outputProcessor.preview?.displayString || ''; isTruncated = outputViewMode === 'schema' ? outputProcessor.schema?.truncated || false : outputProcessor.preview?.truncated || false; } } const showEmptyWarning = !stepFailed && !stepAborted && !isPending && !errorResult && outputViewMode === 'preview' && isEmptyData(outputString || ''); const aborted = stepAborted || (errorResult && isAbortError(stepResult)); return ( <div> {errorResult ? ( aborted ? ( <div className="flex flex-col items-start justify-start p-4 border rounded-lg bg-muted/30 border-border"> <div className="flex items-center gap-2 mb-2"> <OctagonX className="h-4 w-4 text-amber-600 dark:text-amber-400" /> <p className="text-sm font-semibold text-amber-600 dark:text-amber-400">Execution Aborted</p> </div> <p className="text-xs text-muted-foreground"> Step execution was manually stopped. Run the step again to get results. </p> </div> ) : ( <div className="flex flex-col items-start justify-start p-4 border rounded-lg bg-muted/30 border-border"> <div className="flex items-center gap-2 mb-2"> <X className="h-4 w-4 text-red-500 dark:text-red-400" /> <p className="text-sm font-semibold text-red-500 dark:text-red-400">Step Error</p> </div> <pre className="text-xs whitespace-pre-wrap font-mono w-full overflow-x-auto"> {outputString || 'Step execution failed'} </pre> <p className="text-xs text-muted-foreground mt-2"> Use the "Fix Step" button above to automatically repair the step configuration. </p> </div> ) ) : isPending ? ( isActivelyRunning ? ( <div className="flex flex-col items-center justify-center py-8 text-muted-foreground border rounded-md bg-muted/5"> <div className="flex items-center gap-2 mb-1"> <div className="h-3 w-3 animate-spin rounded-full border-2 border-muted-foreground border-t-transparent" /> <span className="text-xs">Currently running...</span> </div> <p className="text-[10px]">Step results will be shown shortly</p> </div> ) : ( <div className="flex flex-col items-center justify-center py-8 text-muted-foreground border rounded-md bg-muted/5"> <div className="text-xs mb-1">No result yet</div> <p className="text-[10px]">Run this step to see results</p> </div> ) ) : outputProcessor.isComputingPreview && !outputProcessor.preview?.displayString ? ( <div className="flex items-center justify-center py-8 border rounded-md bg-muted/5"> <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" /> </div> ) : ( <> <JsonCodeEditor value={outputString} readOnly={true} minHeight="300px" maxHeight="600px" resizable={true} overlay={ <div className="flex items-center gap-1"> {(outputProcessor.isComputingPreview || outputProcessor.isComputingSchema) && ( <Loader2 className="h-4 w-4 animate-spin text-muted-foreground" /> )} <Tabs value={outputViewMode} onValueChange={(v) => handleOutputViewModeChange(v as 'preview' | 'schema')} className="w-auto"> <TabsList className="h-6 p-0.5 rounded-md"> <TabsTrigger value="preview" className="h-full px-2 text-[11px] rounded-sm data-[state=active]:rounded-sm">Preview</TabsTrigger> <TabsTrigger value="schema" className="h-full px-2 text-[11px] rounded-sm data-[state=active]:rounded-sm">Schema</TabsTrigger> </TabsList> </Tabs> <CopyButton text={outputString} /> <Button variant="ghost" size="icon" className="h-6 w-6" onClick={() => downloadJson(stepResult, `step_${step.id}_result.json`)} title="Download step result as JSON" > <Download className="h-3 w-3" /> </Button> </div> } /> {showEmptyWarning && ( <div className="mt-1 text-[11px] text-amber-700 dark:text-amber-300 px-2"> ⚠ No data returned. Is this expected? </div> )} {isTruncated && outputViewMode === 'preview' && ( <div className="mt-1 text-[10px] text-amber-600 dark:text-amber-300 px-2"> Preview truncated for display performance </div> )} </> )} </div> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/superglue-ai/superglue'

If you have feedback or need assistance with the MCP directory API, please join our Discord server