feat: Add intelligent auto-router and enhanced integrations
- Add intelligent-router.sh hook for automatic agent routing - Add AUTO-TRIGGER-SUMMARY.md documentation - Add FINAL-INTEGRATION-SUMMARY.md documentation - Complete Prometheus integration (6 commands + 4 tools) - Complete Dexto integration (12 commands + 5 tools) - Enhanced Ralph with access to all agents - Fix /clawd command (removed disable-model-invocation) - Update hooks.json to v5 with intelligent routing - 291 total skills now available - All 21 commands with automatic routing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* GenericRenderer Component
|
||||
*
|
||||
* Fallback renderer for unknown tool types or when no display data is available.
|
||||
* Displays raw JSON/text content with syntax highlighting.
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { ChevronDown, ChevronRight, Copy, Check } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import hljs from 'highlight.js/lib/core';
|
||||
import json from 'highlight.js/lib/languages/json';
|
||||
|
||||
// Register JSON language
|
||||
hljs.registerLanguage('json', json);
|
||||
|
||||
interface GenericRendererProps {
|
||||
/** Raw content to display */
|
||||
content: unknown;
|
||||
/** Maximum lines before truncation (default: 20) */
|
||||
maxLines?: number;
|
||||
/** Whether to start expanded (default: false) */
|
||||
defaultExpanded?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape HTML entities to prevent XSS when using dangerouslySetInnerHTML
|
||||
*/
|
||||
function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders generic tool result content with syntax highlighting.
|
||||
*/
|
||||
export function GenericRenderer({
|
||||
content,
|
||||
maxLines = 20,
|
||||
defaultExpanded = false,
|
||||
}: GenericRendererProps) {
|
||||
const [expanded, setExpanded] = useState(defaultExpanded);
|
||||
const [showAll, setShowAll] = useState(false);
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
// Format content as string
|
||||
const formattedContent =
|
||||
typeof content === 'string' ? content : JSON.stringify(content, null, 2);
|
||||
|
||||
const lines = formattedContent.split('\n');
|
||||
const shouldTruncate = lines.length > maxLines && !showAll;
|
||||
const displayContent = shouldTruncate ? lines.slice(0, maxLines).join('\n') : formattedContent;
|
||||
|
||||
// Syntax highlight if it looks like JSON, otherwise escape HTML for safety
|
||||
let highlightedContent: string;
|
||||
try {
|
||||
if (typeof content === 'object' || formattedContent.startsWith('{')) {
|
||||
const result = hljs.highlight(displayContent, { language: 'json' });
|
||||
highlightedContent = result.value;
|
||||
} else {
|
||||
// Not JSON - escape HTML entities for plain text
|
||||
highlightedContent = escapeHtml(displayContent);
|
||||
}
|
||||
} catch {
|
||||
// Highlight failed - escape HTML entities for safety
|
||||
highlightedContent = escapeHtml(displayContent);
|
||||
}
|
||||
|
||||
const handleCopy = async () => {
|
||||
await navigator.clipboard.writeText(formattedContent);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
if (!expanded) {
|
||||
return (
|
||||
<button
|
||||
onClick={() => setExpanded(true)}
|
||||
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<ChevronRight className="h-3 w-3" />
|
||||
<span className="font-mono">
|
||||
{lines.length} line{lines.length !== 1 ? 's' : ''} of output
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<button
|
||||
onClick={() => setExpanded(false)}
|
||||
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
<span>Output</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
{copied ? (
|
||||
<>
|
||||
<Check className="h-3 w-3 text-green-500" />
|
||||
<span>Copied</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Copy className="h-3 w-3" />
|
||||
<span>Copy</span>
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-muted/30 rounded-md p-2 overflow-x-auto">
|
||||
<pre
|
||||
className={cn(
|
||||
'text-[10px] font-mono text-foreground/80 whitespace-pre-wrap break-all',
|
||||
'max-h-64 overflow-y-auto scrollbar-thin'
|
||||
)}
|
||||
dangerouslySetInnerHTML={{ __html: highlightedContent }}
|
||||
/>
|
||||
{shouldTruncate && (
|
||||
<button
|
||||
onClick={() => setShowAll(true)}
|
||||
className="mt-2 text-xs text-blue-500 hover:text-blue-600 dark:text-blue-400"
|
||||
>
|
||||
Show {lines.length - maxLines} more lines...
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user