mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-07 16:41:06 +08:00
feat(a2ui): Implement A2UI backend with question handling and WebSocket support
- Added A2UITypes for defining question structures and answers. - Created A2UIWebSocketHandler for managing WebSocket connections and message handling. - Developed ask-question tool for interactive user questions via A2UI. - Introduced platformUtils for platform detection and shell command handling. - Centralized TypeScript types in index.ts for better organization. - Implemented compatibility checks for hook templates based on platform requirements.
This commit is contained in:
194
ccw/frontend/src/components/hook/EventGroup.tsx
Normal file
194
ccw/frontend/src/components/hook/EventGroup.tsx
Normal file
@@ -0,0 +1,194 @@
|
||||
// ========================================
|
||||
// Event Group Component
|
||||
// ========================================
|
||||
// Groups hooks by trigger event type
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import {
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Zap,
|
||||
Wrench,
|
||||
CheckCircle,
|
||||
StopCircle,
|
||||
} from 'lucide-react';
|
||||
import { Card } from '@/components/ui/Card';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Badge } from '@/components/ui/Badge';
|
||||
import { HookCard, type HookCardData, type HookTriggerType } from './HookCard';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ========== Types ==========
|
||||
|
||||
export interface EventGroupProps {
|
||||
eventType: HookTriggerType;
|
||||
hooks: HookCardData[];
|
||||
onHookToggle: (hookName: string, enabled: boolean) => void;
|
||||
onHookEdit: (hook: HookCardData) => void;
|
||||
onHookDelete: (hookName: string) => void;
|
||||
}
|
||||
|
||||
// ========== Helper Functions ==========
|
||||
|
||||
function getEventIcon(eventType: HookTriggerType) {
|
||||
switch (eventType) {
|
||||
case 'UserPromptSubmit':
|
||||
return Zap;
|
||||
case 'PreToolUse':
|
||||
return Wrench;
|
||||
case 'PostToolUse':
|
||||
return CheckCircle;
|
||||
case 'Stop':
|
||||
return StopCircle;
|
||||
}
|
||||
}
|
||||
|
||||
function getEventColor(eventType: HookTriggerType): string {
|
||||
switch (eventType) {
|
||||
case 'UserPromptSubmit':
|
||||
return 'text-amber-500 bg-amber-500/10';
|
||||
case 'PreToolUse':
|
||||
return 'text-blue-500 bg-blue-500/10';
|
||||
case 'PostToolUse':
|
||||
return 'text-green-500 bg-green-500/10';
|
||||
case 'Stop':
|
||||
return 'text-red-500 bg-red-500/10';
|
||||
}
|
||||
}
|
||||
|
||||
// ========== Component ==========
|
||||
|
||||
export function EventGroup({
|
||||
eventType,
|
||||
hooks,
|
||||
onHookToggle,
|
||||
onHookEdit,
|
||||
onHookDelete,
|
||||
}: EventGroupProps) {
|
||||
const { formatMessage } = useIntl();
|
||||
const [isExpanded, setIsExpanded] = useState(true);
|
||||
const [expandedHooks, setExpandedHooks] = useState<Set<string>>(new Set());
|
||||
|
||||
const Icon = getEventIcon(eventType);
|
||||
const iconColorClass = getEventColor(eventType);
|
||||
|
||||
const enabledCount = hooks.filter((h) => h.enabled).length;
|
||||
const totalCount = hooks.length;
|
||||
|
||||
const handleToggleExpand = () => {
|
||||
setIsExpanded(!isExpanded);
|
||||
};
|
||||
|
||||
const handleToggleHookExpand = (hookName: string) => {
|
||||
setExpandedHooks((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(hookName)) {
|
||||
next.delete(hookName);
|
||||
} else {
|
||||
next.add(hookName);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
};
|
||||
|
||||
const handleExpandAll = () => {
|
||||
setExpandedHooks(new Set(hooks.map((h) => h.name)));
|
||||
};
|
||||
|
||||
const handleCollapseAll = () => {
|
||||
setExpandedHooks(new Set());
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="overflow-hidden">
|
||||
{/* Event Header */}
|
||||
<div
|
||||
className="p-4 cursor-pointer hover:bg-muted/50 transition-colors border-b border-border"
|
||||
onClick={handleToggleExpand}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={cn('p-2 rounded-lg', iconColorClass)}>
|
||||
<Icon className="w-5 h-5" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-base font-semibold text-foreground">
|
||||
{formatMessage({ id: `cliHooks.trigger.${eventType}` })}
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{formatMessage({ id: 'cliHooks.stats.count' }, {
|
||||
enabled: enabledCount,
|
||||
total: totalCount
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="text-xs">
|
||||
{totalCount}
|
||||
</Badge>
|
||||
{isExpanded ? (
|
||||
<ChevronUp className="w-5 h-5 text-muted-foreground" />
|
||||
) : (
|
||||
<ChevronDown className="w-5 h-5 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Hooks List */}
|
||||
{isExpanded && (
|
||||
<div className="p-4 space-y-3 bg-muted/10">
|
||||
{totalCount === 0 ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<p className="text-sm">{formatMessage({ id: 'cliHooks.empty.noHooksInEvent' })}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* Expand/Collapse All */}
|
||||
{totalCount > 1 && (
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={handleExpandAll}
|
||||
>
|
||||
{formatMessage({ id: 'cliHooks.actions.expandAll' })}
|
||||
</Button>
|
||||
<span className="text-muted-foreground text-xs">/</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 text-xs"
|
||||
onClick={handleCollapseAll}
|
||||
>
|
||||
{formatMessage({ id: 'cliHooks.actions.collapseAll' })}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Hook Cards */}
|
||||
<div className="space-y-2">
|
||||
{hooks.map((hook) => (
|
||||
<HookCard
|
||||
key={hook.name}
|
||||
hook={hook}
|
||||
isExpanded={expandedHooks.has(hook.name)}
|
||||
onToggleExpand={() => handleToggleHookExpand(hook.name)}
|
||||
onToggle={onHookToggle}
|
||||
onEdit={onHookEdit}
|
||||
onDelete={onHookDelete}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default EventGroup;
|
||||
Reference in New Issue
Block a user