mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-15 02:42:45 +08:00
feat: add terminal panel components and Zustand store for state management
- Created a barrel export file for terminal panel components. - Implemented Zustand store for managing terminal panel UI state, including visibility, active terminal, view mode, and terminal ordering. - Added actions for opening/closing the terminal panel, setting the active terminal, changing view modes, and managing terminal order. - Introduced selectors for accessing terminal panel state properties.
This commit is contained in:
@@ -37,14 +37,6 @@ type ModeType = 'provider-based' | 'direct';
|
|||||||
|
|
||||||
// ========== Helper Functions ==========
|
// ========== Helper Functions ==========
|
||||||
|
|
||||||
function safeStringifyConfig(config: unknown): string {
|
|
||||||
try {
|
|
||||||
return JSON.stringify(config ?? {}, null, 2);
|
|
||||||
} catch {
|
|
||||||
return '{}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseConfigJson(
|
function parseConfigJson(
|
||||||
configJson: string
|
configJson: string
|
||||||
): { ok: true; value: Record<string, unknown> } | { ok: false; errorKey: string } {
|
): { ok: true; value: Record<string, unknown> } | { ok: false; errorKey: string } {
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ describe('ExecutionGroup', () => {
|
|||||||
render(<ExecutionGroup {...defaultProps} type="parallel" />, { locale: 'en' });
|
render(<ExecutionGroup {...defaultProps} type="parallel" />, { locale: 'en' });
|
||||||
|
|
||||||
// Parallel items should not have numbers in the numbering position
|
// Parallel items should not have numbers in the numbering position
|
||||||
const numberElements = document.querySelectorAll('.text-muted-foreground.text-xs');
|
document.querySelectorAll('.text-muted-foreground.text-xs');
|
||||||
// In parallel mode, the numbering position should be empty
|
// In parallel mode, the numbering position should be empty
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,8 +17,13 @@ describe('QueueCard', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mockQueue: IssueQueue = {
|
const mockQueue: IssueQueue = {
|
||||||
tasks: ['task1', 'task2'],
|
tasks: [
|
||||||
solutions: ['solution1'],
|
{ item_id: 'task1', issue_id: 'issue-1', solution_id: 'sol-1', status: 'pending', execution_order: 1, execution_group: 'group-1', depends_on: [], semantic_priority: 1 },
|
||||||
|
{ item_id: 'task2', issue_id: 'issue-1', solution_id: 'sol-1', status: 'pending', execution_order: 2, execution_group: 'group-1', depends_on: [], semantic_priority: 1 },
|
||||||
|
],
|
||||||
|
solutions: [
|
||||||
|
{ item_id: 'solution1', issue_id: 'issue-1', solution_id: 'sol-1', status: 'pending', execution_order: 1, execution_group: 'group-1', depends_on: [], semantic_priority: 1 },
|
||||||
|
],
|
||||||
conflicts: [],
|
conflicts: [],
|
||||||
execution_groups: ['group-1'],
|
execution_groups: ['group-1'],
|
||||||
grouped_items: mockQueueItems,
|
grouped_items: mockQueueItems,
|
||||||
|
|||||||
@@ -127,10 +127,6 @@ export function AppShell({
|
|||||||
return () => window.removeEventListener('resize', handleResize);
|
return () => window.removeEventListener('resize', handleResize);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleMenuClick = useCallback(() => {
|
|
||||||
setMobileOpen((prev) => !prev);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleMobileClose = useCallback(() => {
|
const handleMobileClose = useCallback(() => {
|
||||||
setMobileOpen(false);
|
setMobileOpen(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ describe('Header Component - i18n Tests', () => {
|
|||||||
|
|
||||||
describe('translated aria-labels', () => {
|
describe('translated aria-labels', () => {
|
||||||
it('should have translated aria-label for menu toggle', () => {
|
it('should have translated aria-label for menu toggle', () => {
|
||||||
render(<Header onMenuClick={vi.fn()} />);
|
render(<Header />);
|
||||||
|
|
||||||
const menuButton = screen.getByRole('button', { name: /toggle navigation/i });
|
const menuButton = screen.getByRole('button', { name: /toggle navigation/i });
|
||||||
expect(menuButton).toBeInTheDocument();
|
expect(menuButton).toBeInTheDocument();
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import {
|
|||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from '@/components/ui/AlertDialog';
|
} from '@/components/ui/AlertDialog';
|
||||||
import { Button } from '@/components/ui/Button';
|
|
||||||
import { Badge } from '@/components/ui/Badge';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
// ========== Types ==========
|
// ========== Types ==========
|
||||||
|
|||||||
@@ -30,8 +30,6 @@ interface ServerCheckboxItem {
|
|||||||
selected: boolean;
|
selected: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CopyDirection = 'to-codex' | 'from-codex';
|
|
||||||
|
|
||||||
// ========== Component ==========
|
// ========== Component ==========
|
||||||
|
|
||||||
export function CrossCliSyncPanel({ onSuccess, className }: CrossCliSyncPanelProps) {
|
export function CrossCliSyncPanel({ onSuccess, className }: CrossCliSyncPanelProps) {
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
Globe,
|
Globe,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
Download,
|
Download,
|
||||||
Check,
|
|
||||||
Settings,
|
Settings,
|
||||||
Key,
|
Key,
|
||||||
Zap,
|
Zap,
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ import {
|
|||||||
Loader2,
|
Loader2,
|
||||||
RotateCcw,
|
RotateCcw,
|
||||||
Code,
|
Code,
|
||||||
Image as ImageIcon,
|
|
||||||
Database,
|
Database,
|
||||||
Mail,
|
Mail,
|
||||||
MailOpen,
|
MailOpen,
|
||||||
@@ -67,15 +66,7 @@ function formatTimeAgo(timestamp: string, formatMessage: (message: { id: string;
|
|||||||
return new Date(timestamp).toLocaleDateString();
|
return new Date(timestamp).toLocaleDateString();
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatDetails(details: unknown): string {
|
// ========== Main Types ==========
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
if (typeof details === 'string') return details;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
if (typeof details === 'object' && details !== null) {
|
|
||||||
return JSON.stringify(details, null, 2);
|
|
||||||
}
|
|
||||||
return String(details);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNotificationIcon(type: Toast['type']) {
|
function getNotificationIcon(type: Toast['type']) {
|
||||||
const iconClassName = 'h-4 w-4 shrink-0';
|
const iconClassName = 'h-4 w-4 shrink-0';
|
||||||
@@ -718,7 +709,6 @@ export interface NotificationPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function NotificationPanel({ isOpen, onClose }: NotificationPanelProps) {
|
export function NotificationPanel({ isOpen, onClose }: NotificationPanelProps) {
|
||||||
const { formatMessage } = useIntl();
|
|
||||||
|
|
||||||
// Store state
|
// Store state
|
||||||
const persistentNotifications = useNotificationStore(selectPersistentNotifications);
|
const persistentNotifications = useNotificationStore(selectPersistentNotifications);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
Terminal,
|
Terminal,
|
||||||
Wrench,
|
Wrench,
|
||||||
FileEdit,
|
FileEdit,
|
||||||
|
FileText,
|
||||||
Brain,
|
Brain,
|
||||||
Search,
|
Search,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Vertical timeline displaying tool calls in chronological order
|
// Vertical timeline displaying tool calls in chronological order
|
||||||
|
|
||||||
import React, { memo, useMemo, useCallback, useEffect, useRef } from 'react';
|
import { memo, useMemo, useCallback, useEffect, useRef } from 'react';
|
||||||
import { Wrench, Loader2 } from 'lucide-react';
|
import { Wrench, Loader2 } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { ToolCallCard } from './ToolCallCard';
|
import { ToolCallCard } from './ToolCallCard';
|
||||||
|
|||||||
@@ -10,12 +10,9 @@ import {
|
|||||||
Info,
|
Info,
|
||||||
Code,
|
Code,
|
||||||
Copy,
|
Copy,
|
||||||
ChevronRight,
|
|
||||||
AlertTriangle,
|
AlertTriangle,
|
||||||
Brain,
|
Brain,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/Button';
|
|
||||||
import { Badge } from '@/components/ui/Badge';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { JsonField } from './JsonField';
|
import { JsonField } from './JsonField';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
@@ -94,8 +91,6 @@ const TYPE_CONFIGS: Record<string, TypeConfig> = {
|
|||||||
export function JsonCard({
|
export function JsonCard({
|
||||||
data,
|
data,
|
||||||
type,
|
type,
|
||||||
timestamp,
|
|
||||||
onCopy,
|
|
||||||
}: JsonCardProps) {
|
}: JsonCardProps) {
|
||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export interface JsonFieldProps {
|
|||||||
|
|
||||||
export function JsonField({ fieldName, value }: JsonFieldProps) {
|
export function JsonField({ fieldName, value }: JsonFieldProps) {
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
const [copied, setCopied] = useState(false);
|
const [, setCopied] = useState(false);
|
||||||
|
|
||||||
const isObject = value !== null && typeof value === 'object';
|
const isObject = value !== null && typeof value === 'object';
|
||||||
const isNested = isObject && (Array.isArray(value) || Object.keys(value).length > 0);
|
const isNested = isObject && (Array.isArray(value) || Object.keys(value).length > 0);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Brain, Settings, AlertCircle, Info, MessageCircle, Wrench } from 'lucide-react';
|
import { Brain, Settings, AlertCircle, Info, MessageCircle, Wrench } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import { JsonCard } from './JsonCard';
|
import { JsonCard } from './JsonCard';
|
||||||
import { detectJsonInLine } from '../utils/jsonDetector';
|
import { detectJsonInLine } from '../utils/jsonDetector';
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,6 @@ export function AssistantMessage({
|
|||||||
onCopy,
|
onCopy,
|
||||||
className
|
className
|
||||||
}: AssistantMessageProps) {
|
}: AssistantMessageProps) {
|
||||||
const { formatMessage } = useIntl();
|
|
||||||
const [isExpanded, setIsExpanded] = useState(true);
|
const [isExpanded, setIsExpanded] = useState(true);
|
||||||
const [copied, setCopied] = useState(false);
|
const [copied, setCopied] = useState(false);
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export interface ErrorMessageProps {
|
|||||||
export function ErrorMessage({
|
export function ErrorMessage({
|
||||||
title,
|
title,
|
||||||
message,
|
message,
|
||||||
timestamp,
|
|
||||||
onRetry,
|
onRetry,
|
||||||
onDismiss,
|
onDismiss,
|
||||||
className
|
className
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export interface UserMessageProps {
|
|||||||
|
|
||||||
export function UserMessage({
|
export function UserMessage({
|
||||||
content,
|
content,
|
||||||
timestamp,
|
|
||||||
onCopy,
|
onCopy,
|
||||||
onViewRaw,
|
onViewRaw,
|
||||||
className
|
className
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ interface OutputLineCardProps {
|
|||||||
onCopy?: (content: string) => void;
|
onCopy?: (content: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function OutputLineCard({ group, onCopy }: OutputLineCardProps) {
|
function OutputLineCard({ group }: OutputLineCardProps) {
|
||||||
const borderColor = getBorderColorForType(group.type);
|
const borderColor = getBorderColorForType(group.type);
|
||||||
|
|
||||||
// Extract content from all lines in the group
|
// Extract content from all lines in the group
|
||||||
@@ -357,12 +357,6 @@ export function CliStreamMonitor({ isOpen, onClose }: CliStreamMonitorProps) {
|
|||||||
}, 50); // 50ms debounce
|
}, 50); // 50ms debounce
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Scroll to bottom handler
|
|
||||||
const scrollToBottom = useCallback(() => {
|
|
||||||
logsEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
||||||
setIsUserScrolling(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Handle closing an execution tab
|
// Handle closing an execution tab
|
||||||
const handleCloseExecution = useCallback((executionId: string) => {
|
const handleCloseExecution = useCallback((executionId: string) => {
|
||||||
// Mark as closed by user so it won't be re-added by server sync
|
// Mark as closed by user so it won't be re-added by server sync
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Display detailed view of a single insight with patterns, suggestions, and metadata
|
// Display detailed view of a single insight with patterns, suggestions, and metadata
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
@@ -133,8 +132,7 @@ function formatRelativeTime(timestamp: string, locale: string): string {
|
|||||||
/**
|
/**
|
||||||
* PatternItem component for displaying a single pattern
|
* PatternItem component for displaying a single pattern
|
||||||
*/
|
*/
|
||||||
function PatternItem({ pattern, locale }: { pattern: Pattern; locale: string }) {
|
function PatternItem({ pattern }: { pattern: Pattern; locale: string }) {
|
||||||
const { formatMessage } = useIntl();
|
|
||||||
const severity = pattern.severity ?? 'info';
|
const severity = pattern.severity ?? 'info';
|
||||||
const config = severityConfig[severity] ?? severityConfig.default;
|
const config = severityConfig[severity] ?? severityConfig.default;
|
||||||
|
|
||||||
@@ -177,7 +175,7 @@ function PatternItem({ pattern, locale }: { pattern: Pattern; locale: string })
|
|||||||
/**
|
/**
|
||||||
* SuggestionItem component for displaying a single suggestion
|
* SuggestionItem component for displaying a single suggestion
|
||||||
*/
|
*/
|
||||||
function SuggestionItem({ suggestion, locale }: { suggestion: Suggestion; locale: string }) {
|
function SuggestionItem({ suggestion }: { suggestion: Suggestion; locale: string }) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const config = suggestionTypeConfig[suggestion.type] ?? suggestionTypeConfig.refactor;
|
const config = suggestionTypeConfig[suggestion.type] ?? suggestionTypeConfig.refactor;
|
||||||
const typeLabel = formatMessage({ id: `prompts.suggestions.types.${suggestion.type}` });
|
const typeLabel = formatMessage({ id: `prompts.suggestions.types.${suggestion.type}` });
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// AI insights panel for prompt history analysis
|
// AI insights panel for prompt history analysis
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// LogBlock Component
|
// LogBlock Component
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
import React, { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import {
|
import {
|
||||||
ChevronDown,
|
ChevronDown,
|
||||||
ChevronUp,
|
ChevronUp,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Container component for displaying grouped CLI output blocks
|
// Container component for displaying grouped CLI output blocks
|
||||||
|
|
||||||
import { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { useCliStreamStore, type LogBlockData } from '@/stores/cliStreamStore';
|
import { useCliStreamStore, type LogBlockData } from '@/stores/cliStreamStore';
|
||||||
import { LogBlock } from './LogBlock';
|
import { LogBlock } from './LogBlock';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Statistics display for prompt history
|
// Statistics display for prompt history
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { StatCard, StatCardSkeleton } from '@/components/shared/StatCard';
|
import { StatCard, StatCardSkeleton } from '@/components/shared/StatCard';
|
||||||
import { MessageSquare, FileType, Hash, Star } from 'lucide-react';
|
import { MessageSquare, FileType, Hash, Star } from 'lucide-react';
|
||||||
|
|||||||
@@ -3,14 +3,12 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Real-time scrolling ticker with CSS marquee animation and WebSocket messages
|
// Real-time scrolling ticker with CSS marquee animation and WebSocket messages
|
||||||
|
|
||||||
import * as React from 'react';
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { useRealtimeUpdates, type TickerMessage } from '@/hooks/useRealtimeUpdates';
|
import { useRealtimeUpdates, type TickerMessage } from '@/hooks/useRealtimeUpdates';
|
||||||
import {
|
import {
|
||||||
Play,
|
Play,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
XCircle,
|
|
||||||
Workflow,
|
Workflow,
|
||||||
Activity,
|
Activity,
|
||||||
WifiOff,
|
WifiOff,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Badge } from '../ui/badge';
|
import { Badge } from '../ui/Badge';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { VersionCheckModal } from './VersionCheckModal';
|
import { VersionCheckModal } from './VersionCheckModal';
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import {
|
|||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from '../ui/dialog';
|
} from '../ui/Dialog';
|
||||||
|
|
||||||
interface VersionCheckModalProps {
|
interface VersionCheckModalProps {
|
||||||
currentVersion: string;
|
currentVersion: string;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Team selector, stats chips, and controls
|
// Team selector, stats chips, and controls
|
||||||
|
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Users, MessageSquare, Clock, RefreshCw } from 'lucide-react';
|
import { Users, MessageSquare, RefreshCw } from 'lucide-react';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import { Switch } from '@/components/ui/Switch';
|
import { Switch } from '@/components/ui/Switch';
|
||||||
import { Label } from '@/components/ui/Label';
|
import { Label } from '@/components/ui/Label';
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { useState, useMemo } from 'react';
|
|||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { ChevronDown, ChevronUp, FileText, Filter, X } from 'lucide-react';
|
import { ChevronDown, ChevronUp, FileText, Filter, X } from 'lucide-react';
|
||||||
import { Card, CardContent } from '@/components/ui/Card';
|
import { Card, CardContent } from '@/components/ui/Card';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
@@ -17,7 +16,7 @@ import {
|
|||||||
SelectValue,
|
SelectValue,
|
||||||
} from '@/components/ui/Select';
|
} from '@/components/ui/Select';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import type { TeamMessage, TeamMessageType, TeamMessageFilter } from '@/types/team';
|
import type { TeamMessage, TeamMessageFilter } from '@/types/team';
|
||||||
|
|
||||||
interface TeamMessageFeedProps {
|
interface TeamMessageFeedProps {
|
||||||
messages: TeamMessage[];
|
messages: TeamMessage[];
|
||||||
|
|||||||
@@ -104,18 +104,6 @@ function Arrow() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ForkArrow() {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center px-1">
|
|
||||||
<div className="w-4 h-0.5 bg-border" />
|
|
||||||
<div className="flex flex-col gap-1">
|
|
||||||
<div className="w-3 h-0.5 bg-border -rotate-20" />
|
|
||||||
<div className="w-3 h-0.5 bg-border rotate-20" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function TeamPipeline({ messages }: TeamPipelineProps) {
|
export function TeamPipeline({ messages }: TeamPipelineProps) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const stageStatus = derivePipelineStatus(messages);
|
const stageStatus = derivePipelineStatus(messages);
|
||||||
|
|||||||
323
ccw/frontend/src/components/terminal-panel/TerminalMainArea.tsx
Normal file
323
ccw/frontend/src/components/terminal-panel/TerminalMainArea.tsx
Normal file
@@ -0,0 +1,323 @@
|
|||||||
|
// ========================================
|
||||||
|
// TerminalMainArea Component
|
||||||
|
// ========================================
|
||||||
|
// Main display area for the terminal panel.
|
||||||
|
// Shows header with session info, tab switcher (terminal/queue), and
|
||||||
|
// embedded xterm.js terminal with command input. Reuses the xterm rendering
|
||||||
|
// pattern from IssueTerminalTab (init, FitAddon, output streaming, PTY input).
|
||||||
|
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import { Terminal as XTerm } from 'xterm';
|
||||||
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
|
import { X, Send } from 'lucide-react';
|
||||||
|
import { Button } from '@/components/ui/Button';
|
||||||
|
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/Tabs';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||||
|
import { useTerminalPanelStore } from '@/stores/terminalPanelStore';
|
||||||
|
import { useCliSessionStore } from '@/stores/cliSessionStore';
|
||||||
|
import type { PanelView } from '@/stores/terminalPanelStore';
|
||||||
|
import {
|
||||||
|
fetchCliSessionBuffer,
|
||||||
|
sendCliSessionText,
|
||||||
|
resizeCliSession,
|
||||||
|
executeInCliSession,
|
||||||
|
} from '@/lib/api';
|
||||||
|
|
||||||
|
// ========== Types ==========
|
||||||
|
|
||||||
|
export interface TerminalMainAreaProps {
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Component ==========
|
||||||
|
|
||||||
|
export function TerminalMainArea({ onClose }: TerminalMainAreaProps) {
|
||||||
|
const projectPath = useWorkflowStore(selectProjectPath);
|
||||||
|
|
||||||
|
const activeTerminalId = useTerminalPanelStore((s) => s.activeTerminalId);
|
||||||
|
const panelView = useTerminalPanelStore((s) => s.panelView);
|
||||||
|
const setPanelView = useTerminalPanelStore((s) => s.setPanelView);
|
||||||
|
|
||||||
|
const sessionsByKey = useCliSessionStore((s) => s.sessions);
|
||||||
|
const outputChunks = useCliSessionStore((s) => s.outputChunks);
|
||||||
|
const setBuffer = useCliSessionStore((s) => s.setBuffer);
|
||||||
|
const clearOutput = useCliSessionStore((s) => s.clearOutput);
|
||||||
|
|
||||||
|
const activeSession = activeTerminalId ? sessionsByKey[activeTerminalId] : null;
|
||||||
|
|
||||||
|
const [prompt, setPrompt] = useState('');
|
||||||
|
const [isExecuting, setIsExecuting] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
// xterm refs
|
||||||
|
const terminalHostRef = useRef<HTMLDivElement | null>(null);
|
||||||
|
const xtermRef = useRef<XTerm | null>(null);
|
||||||
|
const fitAddonRef = useRef<FitAddon | null>(null);
|
||||||
|
const lastChunkIndexRef = useRef<number>(0);
|
||||||
|
|
||||||
|
// Input batching refs (same pattern as IssueTerminalTab)
|
||||||
|
const pendingInputRef = useRef<string>('');
|
||||||
|
const flushTimerRef = useRef<number | null>(null);
|
||||||
|
const activeSessionKeyRef = useRef<string | null>(null);
|
||||||
|
|
||||||
|
// Keep ref in sync with activeTerminalId for closures
|
||||||
|
useEffect(() => {
|
||||||
|
activeSessionKeyRef.current = activeTerminalId;
|
||||||
|
}, [activeTerminalId]);
|
||||||
|
|
||||||
|
const flushInput = async () => {
|
||||||
|
const sessionKey = activeSessionKeyRef.current;
|
||||||
|
if (!sessionKey) return;
|
||||||
|
const pending = pendingInputRef.current;
|
||||||
|
pendingInputRef.current = '';
|
||||||
|
if (!pending) return;
|
||||||
|
try {
|
||||||
|
await sendCliSessionText(sessionKey, { text: pending, appendNewline: false }, projectPath || undefined);
|
||||||
|
} catch {
|
||||||
|
// Ignore transient failures
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const scheduleFlush = () => {
|
||||||
|
if (flushTimerRef.current !== null) return;
|
||||||
|
flushTimerRef.current = window.setTimeout(async () => {
|
||||||
|
flushTimerRef.current = null;
|
||||||
|
await flushInput();
|
||||||
|
}, 30);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== xterm Initialization ==========
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!terminalHostRef.current) return;
|
||||||
|
if (xtermRef.current) return;
|
||||||
|
|
||||||
|
const term = new XTerm({
|
||||||
|
convertEol: true,
|
||||||
|
cursorBlink: true,
|
||||||
|
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
||||||
|
fontSize: 12,
|
||||||
|
scrollback: 5000,
|
||||||
|
});
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
term.loadAddon(fitAddon);
|
||||||
|
term.open(terminalHostRef.current);
|
||||||
|
fitAddon.fit();
|
||||||
|
|
||||||
|
// Forward keystrokes to backend (batched)
|
||||||
|
term.onData((data) => {
|
||||||
|
if (!activeSessionKeyRef.current) return;
|
||||||
|
pendingInputRef.current += data;
|
||||||
|
scheduleFlush();
|
||||||
|
});
|
||||||
|
|
||||||
|
xtermRef.current = term;
|
||||||
|
fitAddonRef.current = fitAddon;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
try {
|
||||||
|
term.dispose();
|
||||||
|
} finally {
|
||||||
|
xtermRef.current = null;
|
||||||
|
fitAddonRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// ========== Attach to Active Session ==========
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const term = xtermRef.current;
|
||||||
|
const fitAddon = fitAddonRef.current;
|
||||||
|
if (!term || !fitAddon) return;
|
||||||
|
|
||||||
|
lastChunkIndexRef.current = 0;
|
||||||
|
term.reset();
|
||||||
|
term.clear();
|
||||||
|
|
||||||
|
if (!activeTerminalId) return;
|
||||||
|
clearOutput(activeTerminalId);
|
||||||
|
|
||||||
|
fetchCliSessionBuffer(activeTerminalId, projectPath || undefined)
|
||||||
|
.then(({ buffer }) => {
|
||||||
|
setBuffer(activeTerminalId, buffer || '');
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// ignore
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
fitAddon.fit();
|
||||||
|
});
|
||||||
|
}, [activeTerminalId, projectPath, setBuffer, clearOutput]);
|
||||||
|
|
||||||
|
// ========== Stream Output Chunks ==========
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const term = xtermRef.current;
|
||||||
|
if (!term) return;
|
||||||
|
if (!activeTerminalId) return;
|
||||||
|
|
||||||
|
const chunks = outputChunks[activeTerminalId] ?? [];
|
||||||
|
const start = lastChunkIndexRef.current;
|
||||||
|
if (start >= chunks.length) return;
|
||||||
|
|
||||||
|
for (let i = start; i < chunks.length; i++) {
|
||||||
|
term.write(chunks[i].data);
|
||||||
|
}
|
||||||
|
lastChunkIndexRef.current = chunks.length;
|
||||||
|
}, [outputChunks, activeTerminalId]);
|
||||||
|
|
||||||
|
// ========== Resize Observer ==========
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const host = terminalHostRef.current;
|
||||||
|
const term = xtermRef.current;
|
||||||
|
const fitAddon = fitAddonRef.current;
|
||||||
|
if (!host || !term || !fitAddon) return;
|
||||||
|
|
||||||
|
const resize = () => {
|
||||||
|
fitAddon.fit();
|
||||||
|
const sessionKey = activeSessionKeyRef.current;
|
||||||
|
if (sessionKey) {
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
await resizeCliSession(sessionKey, { cols: term.cols, rows: term.rows }, projectPath || undefined);
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ro = new ResizeObserver(resize);
|
||||||
|
ro.observe(host);
|
||||||
|
return () => ro.disconnect();
|
||||||
|
}, [projectPath]);
|
||||||
|
|
||||||
|
// ========== Execute Command ==========
|
||||||
|
|
||||||
|
const handleExecute = async () => {
|
||||||
|
if (!activeTerminalId) return;
|
||||||
|
if (!prompt.trim()) return;
|
||||||
|
setIsExecuting(true);
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
await executeInCliSession(activeTerminalId, {
|
||||||
|
tool: activeSession?.tool || 'claude',
|
||||||
|
prompt: prompt.trim(),
|
||||||
|
mode: 'analysis',
|
||||||
|
category: 'user',
|
||||||
|
}, projectPath || undefined);
|
||||||
|
setPrompt('');
|
||||||
|
} catch (e) {
|
||||||
|
setError(e instanceof Error ? e.message : String(e));
|
||||||
|
} finally {
|
||||||
|
setIsExecuting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
// Ctrl+Enter or Cmd+Enter to execute
|
||||||
|
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
void handleExecute();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== Render ==========
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex-1 flex flex-col min-w-0">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between px-4 py-3 border-b border-border bg-card">
|
||||||
|
<div className="flex items-center gap-3 min-w-0">
|
||||||
|
<h3 className="text-sm font-semibold text-foreground truncate">
|
||||||
|
{activeSession
|
||||||
|
? `${activeSession.tool || 'cli'} - ${activeSession.sessionKey}`
|
||||||
|
: 'Terminal Panel'}
|
||||||
|
</h3>
|
||||||
|
{activeSession?.tool && (
|
||||||
|
<span className="text-xs text-muted-foreground flex-shrink-0">
|
||||||
|
{activeSession.workingDir}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={onClose}
|
||||||
|
className="flex-shrink-0 hover:bg-secondary"
|
||||||
|
>
|
||||||
|
<X className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Tabs */}
|
||||||
|
<Tabs
|
||||||
|
value={panelView}
|
||||||
|
onValueChange={(v) => setPanelView(v as PanelView)}
|
||||||
|
className="flex-1 flex flex-col min-h-0"
|
||||||
|
>
|
||||||
|
<div className="px-4 pt-2 bg-card">
|
||||||
|
<TabsList>
|
||||||
|
<TabsTrigger value="terminal">Terminal</TabsTrigger>
|
||||||
|
<TabsTrigger value="queue">Queue</TabsTrigger>
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Terminal View */}
|
||||||
|
<TabsContent value="terminal" className="flex-1 flex flex-col min-h-0 mt-0 p-0">
|
||||||
|
{activeTerminalId ? (
|
||||||
|
<div className="flex-1 flex flex-col min-h-0">
|
||||||
|
{/* xterm container */}
|
||||||
|
<div className="flex-1 min-h-0 bg-black/90">
|
||||||
|
<div ref={terminalHostRef} className="h-full w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Command input area */}
|
||||||
|
<div className="border-t border-border p-3 bg-card">
|
||||||
|
{error && (
|
||||||
|
<div className="text-xs text-destructive mb-2">{error}</div>
|
||||||
|
)}
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<textarea
|
||||||
|
value={prompt}
|
||||||
|
onChange={(e) => setPrompt(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
placeholder="Enter command... (Ctrl+Enter to execute)"
|
||||||
|
className={cn(
|
||||||
|
'flex-1 min-h-[60px] max-h-[120px] p-2 bg-background border border-input rounded-md text-sm resize-none',
|
||||||
|
'focus:outline-none focus:ring-2 focus:ring-primary'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={handleExecute}
|
||||||
|
disabled={!activeTerminalId || isExecuting || !prompt.trim()}
|
||||||
|
className="self-end"
|
||||||
|
>
|
||||||
|
<Send className="w-4 h-4 mr-1" />
|
||||||
|
Execute
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1 flex items-center justify-center text-muted-foreground">
|
||||||
|
<p className="text-sm">No terminal selected</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{/* Queue View (placeholder for Phase 2) */}
|
||||||
|
<TabsContent value="queue" className="flex-1 flex items-center justify-center mt-0 p-0">
|
||||||
|
<div className="text-center text-muted-foreground">
|
||||||
|
<p className="text-sm">Execution Queue Management</p>
|
||||||
|
<p className="text-xs mt-1">Coming in Phase 2</p>
|
||||||
|
</div>
|
||||||
|
</TabsContent>
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
142
ccw/frontend/src/components/terminal-panel/TerminalNavBar.tsx
Normal file
142
ccw/frontend/src/components/terminal-panel/TerminalNavBar.tsx
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
// ========================================
|
||||||
|
// TerminalNavBar Component
|
||||||
|
// ========================================
|
||||||
|
// Left navigation bar for the terminal panel.
|
||||||
|
// Shows queue entry icon at top, separator, and dynamic terminal session icons
|
||||||
|
// with status badges. Reads session data from cliSessionStore and panel state
|
||||||
|
// from terminalPanelStore.
|
||||||
|
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import {
|
||||||
|
ClipboardList,
|
||||||
|
Terminal,
|
||||||
|
Loader2,
|
||||||
|
CheckCircle,
|
||||||
|
XCircle,
|
||||||
|
Circle,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { useTerminalPanelStore } from '@/stores/terminalPanelStore';
|
||||||
|
import { useCliSessionStore } from '@/stores/cliSessionStore';
|
||||||
|
|
||||||
|
// ========== Status Badge Configuration ==========
|
||||||
|
|
||||||
|
type SessionStatus = 'running' | 'completed' | 'failed' | 'idle';
|
||||||
|
|
||||||
|
interface StatusBadgeConfig {
|
||||||
|
icon: React.ComponentType<{ className?: string }>;
|
||||||
|
colorClass: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusBadgeMap: Record<SessionStatus, StatusBadgeConfig> = {
|
||||||
|
running: { icon: Loader2, colorClass: 'bg-blue-500' },
|
||||||
|
completed: { icon: CheckCircle, colorClass: 'bg-green-500' },
|
||||||
|
failed: { icon: XCircle, colorClass: 'bg-red-500' },
|
||||||
|
idle: { icon: Circle, colorClass: 'bg-gray-500' },
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== Helpers ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derive a simple session status from the session metadata.
|
||||||
|
* This is a heuristic based on available data - the shellKind and updatedAt fields
|
||||||
|
* provide indirect clues about activity. A more precise status would require
|
||||||
|
* backend support for explicit session state tracking.
|
||||||
|
*/
|
||||||
|
function deriveSessionStatus(_sessionKey: string, _shellKind: string): SessionStatus {
|
||||||
|
// For now, default to idle. In Phase 2 we can refine this
|
||||||
|
// based on active execution tracking from the backend.
|
||||||
|
return 'idle';
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== Component ==========
|
||||||
|
|
||||||
|
export function TerminalNavBar() {
|
||||||
|
const panelView = useTerminalPanelStore((s) => s.panelView);
|
||||||
|
const activeTerminalId = useTerminalPanelStore((s) => s.activeTerminalId);
|
||||||
|
const terminalOrder = useTerminalPanelStore((s) => s.terminalOrder);
|
||||||
|
const setActiveTerminal = useTerminalPanelStore((s) => s.setActiveTerminal);
|
||||||
|
const setPanelView = useTerminalPanelStore((s) => s.setPanelView);
|
||||||
|
|
||||||
|
const sessionsByKey = useCliSessionStore((s) => s.sessions);
|
||||||
|
|
||||||
|
// Build ordered list of sessions that exist in the store
|
||||||
|
const orderedSessions = useMemo(() => {
|
||||||
|
return terminalOrder
|
||||||
|
.map((key) => sessionsByKey[key])
|
||||||
|
.filter(Boolean);
|
||||||
|
}, [terminalOrder, sessionsByKey]);
|
||||||
|
|
||||||
|
const handleQueueClick = () => {
|
||||||
|
setPanelView('queue');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTerminalClick = (sessionKey: string) => {
|
||||||
|
setPanelView('terminal');
|
||||||
|
setActiveTerminal(sessionKey);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-16 flex-shrink-0 flex flex-col border-r border-border bg-muted/30">
|
||||||
|
{/* Queue entry icon */}
|
||||||
|
<div className="flex items-center justify-center py-3">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleQueueClick}
|
||||||
|
className={cn(
|
||||||
|
'w-10 h-10 flex items-center justify-center rounded-md transition-colors',
|
||||||
|
'hover:bg-accent hover:text-accent-foreground',
|
||||||
|
panelView === 'queue' && 'bg-accent text-accent-foreground'
|
||||||
|
)}
|
||||||
|
title="Execution Queue"
|
||||||
|
>
|
||||||
|
<ClipboardList className="w-5 h-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
|
<div className="mx-3 border-t border-border" />
|
||||||
|
|
||||||
|
{/* Terminal session icons (scrollable) */}
|
||||||
|
<div className="flex-1 overflow-y-auto py-2 space-y-1">
|
||||||
|
{orderedSessions.map((session) => {
|
||||||
|
const isActive = activeTerminalId === session.sessionKey && panelView === 'terminal';
|
||||||
|
const status = deriveSessionStatus(session.sessionKey, session.shellKind);
|
||||||
|
const badge = statusBadgeMap[status];
|
||||||
|
const BadgeIcon = badge.icon;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={session.sessionKey} className="flex items-center justify-center">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => handleTerminalClick(session.sessionKey)}
|
||||||
|
className={cn(
|
||||||
|
'relative w-10 h-10 flex items-center justify-center rounded-md transition-colors',
|
||||||
|
'hover:bg-accent hover:text-accent-foreground',
|
||||||
|
isActive && 'bg-accent text-accent-foreground'
|
||||||
|
)}
|
||||||
|
title={`${session.tool || 'cli'} - ${session.sessionKey}`}
|
||||||
|
>
|
||||||
|
<Terminal className="w-5 h-5" />
|
||||||
|
{/* Status badge overlay */}
|
||||||
|
<span
|
||||||
|
className={cn(
|
||||||
|
'absolute bottom-0.5 right-0.5 w-3.5 h-3.5 rounded-full flex items-center justify-center',
|
||||||
|
badge.colorClass
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<BadgeIcon
|
||||||
|
className={cn(
|
||||||
|
'w-2 h-2 text-white',
|
||||||
|
status === 'running' && 'animate-spin'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
71
ccw/frontend/src/components/terminal-panel/TerminalPanel.tsx
Normal file
71
ccw/frontend/src/components/terminal-panel/TerminalPanel.tsx
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
// ========================================
|
||||||
|
// TerminalPanel Component
|
||||||
|
// ========================================
|
||||||
|
// Right-side overlay panel for terminal monitoring.
|
||||||
|
// Follows the IssueDrawer pattern: fixed overlay + translate-x slide animation.
|
||||||
|
// Contains TerminalNavBar (left icon strip) and TerminalMainArea (main content).
|
||||||
|
// All state is read from terminalPanelStore - no props needed.
|
||||||
|
|
||||||
|
import { useEffect, useCallback } from 'react';
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
import { useTerminalPanelStore } from '@/stores/terminalPanelStore';
|
||||||
|
import { TerminalNavBar } from './TerminalNavBar';
|
||||||
|
import { TerminalMainArea } from './TerminalMainArea';
|
||||||
|
|
||||||
|
// ========== Component ==========
|
||||||
|
|
||||||
|
export function TerminalPanel() {
|
||||||
|
const isPanelOpen = useTerminalPanelStore((s) => s.isPanelOpen);
|
||||||
|
const closePanel = useTerminalPanelStore((s) => s.closePanel);
|
||||||
|
|
||||||
|
const handleClose = useCallback(() => {
|
||||||
|
closePanel();
|
||||||
|
}, [closePanel]);
|
||||||
|
|
||||||
|
// ESC key to close
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isPanelOpen) return;
|
||||||
|
const handleEsc = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Escape') handleClose();
|
||||||
|
};
|
||||||
|
window.addEventListener('keydown', handleEsc);
|
||||||
|
return () => window.removeEventListener('keydown', handleEsc);
|
||||||
|
}, [isPanelOpen, handleClose]);
|
||||||
|
|
||||||
|
if (!isPanelOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* Overlay */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'fixed inset-0 bg-black/40 transition-opacity z-40',
|
||||||
|
isPanelOpen ? 'opacity-100' : 'opacity-0 pointer-events-none'
|
||||||
|
)}
|
||||||
|
onClick={handleClose}
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Panel */}
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
'fixed top-0 right-0 h-full w-1/2 bg-background border-l border-border shadow-2xl z-50',
|
||||||
|
'flex flex-row transition-transform duration-300 ease-in-out',
|
||||||
|
isPanelOpen ? 'translate-x-0' : 'translate-x-full'
|
||||||
|
)}
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label="Terminal Panel"
|
||||||
|
style={{ minWidth: '400px', maxWidth: '800px' }}
|
||||||
|
>
|
||||||
|
{/* Left navigation bar */}
|
||||||
|
<TerminalNavBar />
|
||||||
|
|
||||||
|
{/* Main display area */}
|
||||||
|
<TerminalMainArea onClose={handleClose} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
8
ccw/frontend/src/components/terminal-panel/index.ts
Normal file
8
ccw/frontend/src/components/terminal-panel/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// ========================================
|
||||||
|
// Terminal Panel - Barrel Exports
|
||||||
|
// ========================================
|
||||||
|
// Re-exports all terminal panel components for convenient imports.
|
||||||
|
|
||||||
|
export { TerminalPanel } from './TerminalPanel';
|
||||||
|
export { TerminalNavBar } from './TerminalNavBar';
|
||||||
|
export { TerminalMainArea } from './TerminalMainArea';
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { Info, Plus, Trash2 } from "lucide-react";
|
import { Info, Trash2 } from "lucide-react";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
export interface ContextRule {
|
export interface ContextRule {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export interface MultiNodeSelectorProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MultiNodeSelector = React.forwardRef<HTMLDivElement, MultiNodeSelectorProps>(
|
const MultiNodeSelector = React.forwardRef<HTMLDivElement, MultiNodeSelectorProps>(
|
||||||
({ availableNodes, selectedNodes, onChange, placeholder, emptyMessage, className }, ref) => {
|
({ availableNodes, selectedNodes, onChange, emptyMessage, className }, ref) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
const isSelected = (nodeId: string) => selectedNodes.includes(nodeId);
|
const isSelected = (nodeId: string) => selectedNodes.includes(nodeId);
|
||||||
|
|||||||
@@ -108,7 +108,6 @@ export function useActiveCliExecutions(
|
|||||||
const removeExecution = useCliStreamStore(state => state.removeExecution);
|
const removeExecution = useCliStreamStore(state => state.removeExecution);
|
||||||
const executions = useCliStreamStore(state => state.executions);
|
const executions = useCliStreamStore(state => state.executions);
|
||||||
const setCurrentExecution = useCliStreamStore(state => state.setCurrentExecution);
|
const setCurrentExecution = useCliStreamStore(state => state.setCurrentExecution);
|
||||||
const markExecutionClosedByUser = useCliStreamStore(state => state.markExecutionClosedByUser);
|
|
||||||
const isExecutionClosedByUser = useCliStreamStore(state => state.isExecutionClosedByUser);
|
const isExecutionClosedByUser = useCliStreamStore(state => state.isExecutionClosedByUser);
|
||||||
const cleanupUserClosedExecutions = useCliStreamStore(state => state.cleanupUserClosedExecutions);
|
const cleanupUserClosedExecutions = useCliStreamStore(state => state.cleanupUserClosedExecutions);
|
||||||
|
|
||||||
|
|||||||
@@ -44,10 +44,8 @@ import {
|
|||||||
type ProviderCredential,
|
type ProviderCredential,
|
||||||
type CustomEndpoint,
|
type CustomEndpoint,
|
||||||
type CacheStats,
|
type CacheStats,
|
||||||
type GlobalCacheSettings,
|
|
||||||
type ModelPoolConfig,
|
type ModelPoolConfig,
|
||||||
type ModelPoolType,
|
type ModelPoolType,
|
||||||
type DiscoveredProvider,
|
|
||||||
type CliSettingsEndpoint,
|
type CliSettingsEndpoint,
|
||||||
type SaveCliSettingsRequest,
|
type SaveCliSettingsRequest,
|
||||||
} from '../lib/api';
|
} from '../lib/api';
|
||||||
|
|||||||
@@ -9,15 +9,10 @@ import {
|
|||||||
fetchGraphImpact,
|
fetchGraphImpact,
|
||||||
type GraphDependenciesRequest,
|
type GraphDependenciesRequest,
|
||||||
type GraphDependenciesResponse,
|
type GraphDependenciesResponse,
|
||||||
type GraphImpactRequest,
|
|
||||||
type GraphImpactResponse,
|
|
||||||
} from '../lib/api';
|
} from '../lib/api';
|
||||||
import type {
|
import type {
|
||||||
GraphData,
|
GraphData,
|
||||||
GraphNode,
|
|
||||||
GraphEdge,
|
|
||||||
GraphFilters,
|
GraphFilters,
|
||||||
GraphMetadata,
|
|
||||||
NodeType,
|
NodeType,
|
||||||
EdgeType,
|
EdgeType,
|
||||||
} from '../types/graph-explorer';
|
} from '../types/graph-explorer';
|
||||||
@@ -132,7 +127,7 @@ function filterGraphData(
|
|||||||
|
|
||||||
// Filter by minimum complexity
|
// Filter by minimum complexity
|
||||||
if (filters.minComplexity !== undefined) {
|
if (filters.minComplexity !== undefined) {
|
||||||
filteredNodes = filteredNodes.filter(node => {
|
filteredNodes = filteredNodes.filter(_node => {
|
||||||
// This would require complexity data to be available
|
// This would require complexity data to be available
|
||||||
// For now, we'll skip this filter
|
// For now, we'll skip this filter
|
||||||
return true;
|
return true;
|
||||||
@@ -239,7 +234,6 @@ export function useGraphData(options: UseGraphDataOptions = {}): UseGraphDataRet
|
|||||||
rootPath,
|
rootPath,
|
||||||
maxDepth,
|
maxDepth,
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
edgeTypes,
|
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { renderHook, waitFor } from '@testing-library/react';
|
|||||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||||
import {
|
import {
|
||||||
useIssueQueue,
|
useIssueQueue,
|
||||||
useIssueMutations,
|
|
||||||
useQueueMutations,
|
useQueueMutations,
|
||||||
useIssueDiscovery,
|
useIssueDiscovery,
|
||||||
} from './useIssues';
|
} from './useIssues';
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import {
|
|||||||
exportDiscoveryFindingsAsIssues,
|
exportDiscoveryFindingsAsIssues,
|
||||||
type Issue,
|
type Issue,
|
||||||
type IssueQueue,
|
type IssueQueue,
|
||||||
type IssuesResponse,
|
|
||||||
type QueueHistoryIndex,
|
type QueueHistoryIndex,
|
||||||
type DiscoverySession,
|
type DiscoverySession,
|
||||||
type Finding,
|
type Finding,
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import {
|
|||||||
type McpProjectConfigType,
|
type McpProjectConfigType,
|
||||||
type McpTemplate,
|
type McpTemplate,
|
||||||
type McpTemplateInstallRequest,
|
type McpTemplateInstallRequest,
|
||||||
type AllProjectsResponse,
|
|
||||||
type OtherProjectsServersResponse,
|
type OtherProjectsServersResponse,
|
||||||
type CrossCliCopyRequest,
|
type CrossCliCopyRequest,
|
||||||
type CrossCliCopyResponse,
|
type CrossCliCopyResponse,
|
||||||
@@ -439,7 +438,7 @@ export function useCodexMutations(): UseCodexMutationsReturn {
|
|||||||
// Optimistic update could be added here if needed
|
// Optimistic update could be added here if needed
|
||||||
return { serverName, enabled };
|
return { serverName, enabled };
|
||||||
},
|
},
|
||||||
onError: (_error, _vars, context) => {
|
onError: (_error, _vars, _context) => {
|
||||||
// Rollback on error
|
// Rollback on error
|
||||||
console.error('Failed to toggle Codex MCP server:', _error);
|
console.error('Failed to toggle Codex MCP server:', _error);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ export function useCreateSession(): UseCreateSessionReturn {
|
|||||||
|
|
||||||
const mutation = useMutation({
|
const mutation = useMutation({
|
||||||
mutationFn: createSession,
|
mutationFn: createSession,
|
||||||
onSuccess: (newSession) => {
|
onSuccess: () => {
|
||||||
// Invalidate sessions cache to trigger refetch
|
// Invalidate sessions cache to trigger refetch
|
||||||
queryClient.invalidateQueries({ queryKey: ['workspace'] });
|
queryClient.invalidateQueries({ queryKey: ['workspace'] });
|
||||||
// Invalidate dashboard stats
|
// Invalidate dashboard stats
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
enableSkill,
|
enableSkill,
|
||||||
disableSkill,
|
disableSkill,
|
||||||
type Skill,
|
type Skill,
|
||||||
type SkillsResponse,
|
|
||||||
} from '../lib/api';
|
} from '../lib/api';
|
||||||
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||||
import { workspaceQueryKeys } from '@/lib/queryKeys';
|
import { workspaceQueryKeys } from '@/lib/queryKeys';
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
type ExecutionLog,
|
type ExecutionLog,
|
||||||
} from '../types/execution';
|
} from '../types/execution';
|
||||||
import { SurfaceUpdateSchema } from '../packages/a2ui-runtime/core/A2UITypes';
|
import { SurfaceUpdateSchema } from '../packages/a2ui-runtime/core/A2UITypes';
|
||||||
import type { ToolCallKind } from '../types/toolCall';
|
import type { ToolCallKind, ToolCallExecution } from '../types/toolCall';
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const RECONNECT_DELAY_BASE = 1000; // 1 second
|
const RECONNECT_DELAY_BASE = 1000; // 1 second
|
||||||
@@ -242,7 +242,7 @@ export function useWebSocket(options: UseWebSocketOptions = {}): UseWebSocketRet
|
|||||||
const currentNodeId = stores.currentExecution?.currentNodeId;
|
const currentNodeId = stores.currentExecution?.currentNodeId;
|
||||||
if (currentNodeId && (unitType === 'stdout' || unitType === 'stderr')) {
|
if (currentNodeId && (unitType === 'stdout' || unitType === 'stderr')) {
|
||||||
const toolCalls = stores.getToolCallsForNode?.(currentNodeId);
|
const toolCalls = stores.getToolCallsForNode?.(currentNodeId);
|
||||||
const activeCall = toolCalls?.find(c => c.status === 'executing');
|
const activeCall = toolCalls?.find((c: ToolCallExecution) => c.status === 'executing');
|
||||||
|
|
||||||
if (activeCall) {
|
if (activeCall) {
|
||||||
stores.updateToolCall(currentNodeId, activeCall.callId, {
|
stores.updateToolCall(currentNodeId, activeCall.callId, {
|
||||||
|
|||||||
@@ -3,9 +3,8 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// TanStack Query hook for fetching workflow status distribution
|
// TanStack Query hook for fetching workflow status distribution
|
||||||
|
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore';
|
||||||
import { workspaceQueryKeys } from '@/lib/queryKeys';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Workflow status count data structure
|
* Workflow status count data structure
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ describe('MCP API (frontend ↔ backend contract)', () => {
|
|||||||
it('toggleMcpServer uses /api/mcp-toggle with { projectPath, serverName, enable }', async () => {
|
it('toggleMcpServer uses /api/mcp-toggle with { projectPath, serverName, enable }', async () => {
|
||||||
const fetchMock = vi
|
const fetchMock = vi
|
||||||
.spyOn(globalThis, 'fetch')
|
.spyOn(globalThis, 'fetch')
|
||||||
.mockImplementation(async (input, init) => {
|
.mockImplementation(async (input, _init) => {
|
||||||
if (input === '/api/mcp-toggle') {
|
if (input === '/api/mcp-toggle') {
|
||||||
return jsonResponse({ success: true, serverName: 'global1', enabled: false });
|
return jsonResponse({ success: true, serverName: 'global1', enabled: false });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ describe('Component Renderer Interface', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should support async action handlers', async () => {
|
it('should support async action handlers', async () => {
|
||||||
const asyncAction: ActionHandler = async (actionId, params) => {
|
const asyncAction: ActionHandler = async (_actionId, _params) => {
|
||||||
await Promise.resolve();
|
await Promise.resolve();
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
|
|
||||||
import { describe, it, expect } from 'vitest';
|
import { describe, it, expect } from 'vitest';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { A2UIParser, a2uiParser, A2UIParseError } from '../core/A2UIParser';
|
import { a2uiParser, A2UIParseError } from '../core/A2UIParser';
|
||||||
import type { SurfaceUpdate, A2UIComponent } from '../core/A2UITypes';
|
|
||||||
|
|
||||||
// Import component renderers to trigger auto-registration
|
// Import component renderers to trigger auto-registration
|
||||||
import '../renderer/components';
|
import '../renderer/components';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Tests for all A2UI component renderers
|
// Tests for all A2UI component renderers
|
||||||
|
|
||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
import { render, screen, cleanup, within } from '@testing-library/react';
|
import { render, screen, cleanup } from '@testing-library/react';
|
||||||
import type { A2UIComponent } from '../core/A2UITypes';
|
import type { A2UIComponent } from '../core/A2UITypes';
|
||||||
import type { A2UIState, ActionHandler, BindingResolver } from '../core/A2UIComponentRegistry';
|
import type { A2UIState, ActionHandler, BindingResolver } from '../core/A2UIComponentRegistry';
|
||||||
import type { TextComponent, ButtonComponent, DropdownComponent, CLIOutputComponent, DateTimeInputComponent } from '../core/A2UITypes';
|
import type { TextComponent, ButtonComponent, DropdownComponent, CLIOutputComponent, DateTimeInputComponent } from '../core/A2UITypes';
|
||||||
@@ -653,7 +653,7 @@ describe('A2UI Component Integration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle async action handlers', async () => {
|
it('should handle async action handlers', async () => {
|
||||||
const asyncOnAction: ActionHandler = async (actionId, params) => {
|
const asyncOnAction: ActionHandler = async (_actionId, _params) => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// React component that renders A2UI surfaces
|
// React component that renders A2UI surfaces
|
||||||
|
|
||||||
import React, { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import type { SurfaceUpdate, SurfaceComponent, A2UIComponent, LiteralString, Binding } from '../core/A2UITypes';
|
import type { SurfaceUpdate, A2UIComponent, LiteralString, Binding } from '../core/A2UITypes';
|
||||||
import { a2uiRegistry, type A2UIState, type ActionHandler, type BindingResolver } from '../core/A2UIComponentRegistry';
|
import { a2uiRegistry, type A2UIState, type ActionHandler, type BindingResolver } from '../core/A2UIComponentRegistry';
|
||||||
|
|
||||||
// ========== Renderer Props ==========
|
// ========== Renderer Props ==========
|
||||||
@@ -26,7 +26,7 @@ interface A2UIRendererProps {
|
|||||||
*/
|
*/
|
||||||
export function A2UIRenderer({ surface, onAction, className = '' }: A2UIRendererProps) {
|
export function A2UIRenderer({ surface, onAction, className = '' }: A2UIRendererProps) {
|
||||||
// Local state initialized with surface's initial state
|
// Local state initialized with surface's initial state
|
||||||
const [localState, setLocalState] = useState<A2UIState>(surface.initialState || {});
|
const [localState] = useState<A2UIState>(surface.initialState || {});
|
||||||
|
|
||||||
// Handle action from components
|
// Handle action from components
|
||||||
const handleAction = useCallback<ActionHandler>(
|
const handleAction = useCallback<ActionHandler>(
|
||||||
@@ -57,21 +57,6 @@ export function A2UIRenderer({ surface, onAction, className = '' }: A2UIRenderer
|
|||||||
[localState]
|
[localState]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update state from external source
|
|
||||||
const updateState = useCallback((updates: Partial<A2UIState>) => {
|
|
||||||
setLocalState((prev) => ({ ...prev, ...updates }));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
// Memoize context for components
|
|
||||||
const contextValue = useMemo(
|
|
||||||
() => ({
|
|
||||||
state: localState,
|
|
||||||
resolveBinding,
|
|
||||||
updateState,
|
|
||||||
}),
|
|
||||||
[localState, resolveBinding, updateState]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`a2ui-surface ${className}`} data-surface-id={surface.surfaceId}>
|
<div className={`a2ui-surface ${className}`} data-surface-id={surface.surfaceId}>
|
||||||
{surface.components.map((comp) => (
|
{surface.components.map((comp) => (
|
||||||
|
|||||||
@@ -3,25 +3,16 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Maps A2UI Button component to shadcn/ui Button
|
// Maps A2UI Button component to shadcn/ui Button
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import type { A2UIState, ActionHandler, BindingResolver } from '../../core/A2UIComponentRegistry';
|
import type { ButtonComponent } from '../../core/A2UITypes';
|
||||||
import type { ButtonComponent, A2UIComponent } from '../../core/A2UITypes';
|
|
||||||
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
||||||
|
|
||||||
interface A2UIButtonRendererProps {
|
|
||||||
component: A2UIComponent;
|
|
||||||
state: A2UIState;
|
|
||||||
onAction: ActionHandler;
|
|
||||||
resolveBinding: BindingResolver;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI Button Component Renderer
|
* A2UI Button Component Renderer
|
||||||
* Maps A2UI variants (primary/secondary/destructive) to shadcn/ui variants (default/secondary/destructive/ghost)
|
* Maps A2UI variants (primary/secondary/destructive) to shadcn/ui variants (default/secondary/destructive/ghost)
|
||||||
*/
|
*/
|
||||||
export const A2UIButton: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UIButton: ComponentRenderer = ({ component, onAction, resolveBinding }) => {
|
||||||
const buttonComp = component as ButtonComponent;
|
const buttonComp = component as ButtonComponent;
|
||||||
const { Button: buttonConfig } = buttonComp;
|
const { Button: buttonConfig } = buttonComp;
|
||||||
|
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ function StreamingIndicator() {
|
|||||||
* A2UI CLIOutput Component Renderer
|
* A2UI CLIOutput Component Renderer
|
||||||
* Displays CLI output with optional syntax highlighting and streaming indicator
|
* Displays CLI output with optional syntax highlighting and streaming indicator
|
||||||
*/
|
*/
|
||||||
export const A2UICLIOutput: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UICLIOutput: ComponentRenderer = ({ component, resolveBinding }) => {
|
||||||
const cliOutputComp = component as CLIOutputComponent;
|
const cliOutputComp = component as CLIOutputComponent;
|
||||||
const { CLIOutput: config } = cliOutputComp;
|
const { CLIOutput: config } = cliOutputComp;
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,16 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Maps A2UI Card component to shadcn/ui Card
|
// Maps A2UI Card component to shadcn/ui Card
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/Card';
|
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '@/components/ui/Card';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveTextContent } from '../A2UIRenderer';
|
import { resolveTextContent } from '../A2UIRenderer';
|
||||||
import type { CardComponent } from '../../core/A2UITypes';
|
import type { CardComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
interface A2UICardProps {
|
|
||||||
component: CardComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI Card Component Renderer
|
* A2UI Card Component Renderer
|
||||||
* Container component with optional title and description
|
* Container component with optional title and description
|
||||||
*/
|
*/
|
||||||
export const A2UICard: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UICard: ComponentRenderer = ({ component, resolveBinding }) => {
|
||||||
const cardComp = component as CardComponent;
|
const cardComp = component as CardComponent;
|
||||||
const { Card: cardConfig } = cardComp;
|
const { Card: cardConfig } = cardComp;
|
||||||
|
|
||||||
|
|||||||
@@ -3,25 +3,18 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Maps A2UI Checkbox component to shadcn/ui Checkbox
|
// Maps A2UI Checkbox component to shadcn/ui Checkbox
|
||||||
|
|
||||||
import React, { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Checkbox } from '@/components/ui/Checkbox';
|
import { Checkbox } from '@/components/ui/Checkbox';
|
||||||
import { Label } from '@/components/ui/Label';
|
import { Label } from '@/components/ui/Label';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveLiteralOrBinding, resolveTextContent } from '../A2UIRenderer';
|
import { resolveLiteralOrBinding, resolveTextContent } from '../A2UIRenderer';
|
||||||
import type { CheckboxComponent } from '../../core/A2UITypes';
|
import type { CheckboxComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
interface A2UICheckboxProps {
|
|
||||||
component: CheckboxComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI Checkbox Component Renderer
|
* A2UI Checkbox Component Renderer
|
||||||
* Boolean state binding with onChange handler
|
* Boolean state binding with onChange handler
|
||||||
*/
|
*/
|
||||||
export const A2UICheckbox: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UICheckbox: ComponentRenderer = ({ component, onAction, resolveBinding }) => {
|
||||||
const checkboxComp = component as CheckboxComponent;
|
const checkboxComp = component as CheckboxComponent;
|
||||||
const { Checkbox: checkboxConfig } = checkboxComp;
|
const { Checkbox: checkboxConfig } = checkboxComp;
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Date/time picker with ISO string format support
|
// Date/time picker with ISO string format support
|
||||||
|
|
||||||
import React, { useState, useCallback, useEffect } from 'react';
|
import { useState, useCallback, useEffect } from 'react';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveLiteralOrBinding, resolveTextContent } from '../A2UIRenderer';
|
import { resolveTextContent } from '../A2UIRenderer';
|
||||||
import type { DateTimeInputComponent } from '../../core/A2UITypes';
|
import type { DateTimeInputComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -30,7 +30,7 @@ function isoToDateTimeLocal(isoString: string): string {
|
|||||||
/**
|
/**
|
||||||
* Convert datetime-local input format to ISO string
|
* Convert datetime-local input format to ISO string
|
||||||
*/
|
*/
|
||||||
function dateTimeLocalToIso(dateTimeLocal: string, includeTime: boolean): string {
|
function dateTimeLocalToIso(dateTimeLocal: string, _includeTime: boolean): string {
|
||||||
if (!dateTimeLocal) return '';
|
if (!dateTimeLocal) return '';
|
||||||
|
|
||||||
const date = new Date(dateTimeLocal);
|
const date = new Date(dateTimeLocal);
|
||||||
@@ -43,7 +43,7 @@ function dateTimeLocalToIso(dateTimeLocal: string, includeTime: boolean): string
|
|||||||
* A2UI DateTimeInput Component Renderer
|
* A2UI DateTimeInput Component Renderer
|
||||||
* Uses native input[type="datetime-local"] or input[type="date"] based on includeTime
|
* Uses native input[type="datetime-local"] or input[type="date"] based on includeTime
|
||||||
*/
|
*/
|
||||||
export const A2UIDateTimeInput: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UIDateTimeInput: ComponentRenderer = ({ component, onAction, resolveBinding }) => {
|
||||||
const dateTimeComp = component as DateTimeInputComponent;
|
const dateTimeComp = component as DateTimeInputComponent;
|
||||||
const { DateTimeInput: config } = dateTimeComp;
|
const { DateTimeInput: config } = dateTimeComp;
|
||||||
const includeTime = config.includeTime ?? true;
|
const includeTime = config.includeTime ?? true;
|
||||||
|
|||||||
@@ -3,24 +3,16 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Maps A2UI Progress component to shadcn/ui Progress
|
// Maps A2UI Progress component to shadcn/ui Progress
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
import { Progress } from '@/components/ui/Progress';
|
import { Progress } from '@/components/ui/Progress';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
||||||
import type { ProgressComponent } from '../../core/A2UITypes';
|
import type { ProgressComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
interface A2UIProgressProps {
|
|
||||||
component: ProgressComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI Progress Component Renderer
|
* A2UI Progress Component Renderer
|
||||||
* For CLI output progress display
|
* For CLI output progress display
|
||||||
*/
|
*/
|
||||||
export const A2UIProgress: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UIProgress: ComponentRenderer = ({ component, resolveBinding }) => {
|
||||||
const progressComp = component as ProgressComponent;
|
const progressComp = component as ProgressComponent;
|
||||||
const { Progress: progressConfig } = progressComp;
|
const { Progress: progressConfig } = progressComp;
|
||||||
|
|
||||||
|
|||||||
@@ -4,25 +4,18 @@
|
|||||||
// Maps A2UI RadioGroup component to shadcn/ui RadioGroup
|
// Maps A2UI RadioGroup component to shadcn/ui RadioGroup
|
||||||
// Used for single-select questions with visible options
|
// Used for single-select questions with visible options
|
||||||
|
|
||||||
import React, { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { RadioGroup, RadioGroupItem } from '@/components/ui/RadioGroup';
|
import { RadioGroup, RadioGroupItem } from '@/components/ui/RadioGroup';
|
||||||
import { Label } from '@/components/ui/Label';
|
import { Label } from '@/components/ui/Label';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveLiteralOrBinding, resolveTextContent } from '../A2UIRenderer';
|
import { resolveLiteralOrBinding, resolveTextContent } from '../A2UIRenderer';
|
||||||
import type { RadioGroupComponent } from '../../core/A2UITypes';
|
import type { RadioGroupComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
interface A2UIRadioGroupProps {
|
|
||||||
component: RadioGroupComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI RadioGroup Component Renderer
|
* A2UI RadioGroup Component Renderer
|
||||||
* Single selection from visible options with onChange handler
|
* Single selection from visible options with onChange handler
|
||||||
*/
|
*/
|
||||||
export const A2UIRadioGroup: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UIRadioGroup: ComponentRenderer = ({ component, onAction, resolveBinding }) => {
|
||||||
const radioGroupComp = component as RadioGroupComponent;
|
const radioGroupComp = component as RadioGroupComponent;
|
||||||
const { RadioGroup: radioConfig } = radioGroupComp;
|
const { RadioGroup: radioConfig } = radioGroupComp;
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveTextContent } from '../A2UIRenderer';
|
import { resolveTextContent } from '../A2UIRenderer';
|
||||||
import type { TextComponent } from '../../core/A2UITypes';
|
|
||||||
|
|
||||||
interface A2UITextProps {
|
|
||||||
component: TextComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI Text Component Renderer
|
* A2UI Text Component Renderer
|
||||||
* Maps A2UI Text usageHint to HTML elements (h1, h2, h3, p, span, code)
|
* Maps A2UI Text usageHint to HTML elements (h1, h2, h3, p, span, code)
|
||||||
*/
|
*/
|
||||||
export const A2UIText: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UIText: ComponentRenderer = ({ component, resolveBinding }) => {
|
||||||
const { Text } = component as { Text: { text: unknown; usageHint?: string } };
|
const { Text } = component as { Text: { text: unknown; usageHint?: string } };
|
||||||
|
|
||||||
// Resolve text content
|
// Resolve text content
|
||||||
|
|||||||
@@ -3,24 +3,17 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Maps A2UI TextArea component to shadcn/ui Textarea
|
// Maps A2UI TextArea component to shadcn/ui Textarea
|
||||||
|
|
||||||
import React, { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Textarea } from '@/components/ui/Textarea';
|
import { Textarea } from '@/components/ui/Textarea';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
||||||
import type { TextAreaComponent } from '../../core/A2UITypes';
|
import type { TextAreaComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
interface A2UITextAreaProps {
|
|
||||||
component: TextAreaComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI TextArea Component Renderer
|
* A2UI TextArea Component Renderer
|
||||||
* Two-way binding via onChange updates to local state
|
* Two-way binding via onChange updates to local state
|
||||||
*/
|
*/
|
||||||
export const A2UITextArea: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UITextArea: ComponentRenderer = ({ component, onAction, resolveBinding }) => {
|
||||||
const areaComp = component as TextAreaComponent;
|
const areaComp = component as TextAreaComponent;
|
||||||
const { TextArea: areaConfig } = areaComp;
|
const { TextArea: areaConfig } = areaComp;
|
||||||
|
|
||||||
|
|||||||
@@ -3,24 +3,17 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Maps A2UI TextField component to shadcn/ui Input
|
// Maps A2UI TextField component to shadcn/ui Input
|
||||||
|
|
||||||
import React, { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { Input } from '@/components/ui/Input';
|
import { Input } from '@/components/ui/Input';
|
||||||
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
import type { ComponentRenderer } from '../../core/A2UIComponentRegistry';
|
||||||
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
import { resolveLiteralOrBinding } from '../A2UIRenderer';
|
||||||
import type { TextFieldComponent } from '../../core/A2UITypes';
|
import type { TextFieldComponent } from '../../core/A2UITypes';
|
||||||
|
|
||||||
interface A2UITextFieldProps {
|
|
||||||
component: TextFieldComponent;
|
|
||||||
state: Record<string, unknown>;
|
|
||||||
onAction: (actionId: string, params: Record<string, unknown>) => void | Promise<void>;
|
|
||||||
resolveBinding: (binding: { path: string }) => unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A2UI TextField Component Renderer
|
* A2UI TextField Component Renderer
|
||||||
* Two-way binding via onChange updates to local state
|
* Two-way binding via onChange updates to local state
|
||||||
*/
|
*/
|
||||||
export const A2UITextField: ComponentRenderer = ({ component, state, onAction, resolveBinding }) => {
|
export const A2UITextField: ComponentRenderer = ({ component, onAction, resolveBinding }) => {
|
||||||
const fieldComp = component as TextFieldComponent;
|
const fieldComp = component as TextFieldComponent;
|
||||||
const { TextField: fieldConfig } = fieldComp;
|
const { TextField: fieldConfig } = fieldComp;
|
||||||
|
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ export function CliViewerPage() {
|
|||||||
const lastMessage = useNotificationStore(selectWsLastMessage);
|
const lastMessage = useNotificationStore(selectWsLastMessage);
|
||||||
|
|
||||||
// Active execution sync from server
|
// Active execution sync from server
|
||||||
const { isLoading: isSyncing } = useActiveCliExecutions(true); // Always sync when page is open
|
const { isLoading: _isSyncing } = useActiveCliExecutions(true); // Always sync when page is open
|
||||||
const invalidateActive = useInvalidateActiveCliExecutions();
|
const invalidateActive = useInvalidateActiveCliExecutions();
|
||||||
|
|
||||||
// Detect current layout type from store
|
// Detect current layout type from store
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|||||||
import { render, screen, waitFor } from '@/test/i18n';
|
import { render, screen, waitFor } from '@/test/i18n';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
import { CodexLensManagerPage } from './CodexLensManagerPage';
|
import { CodexLensManagerPage } from './CodexLensManagerPage';
|
||||||
import * as api from '@/lib/api';
|
|
||||||
|
|
||||||
// Mock api module
|
// Mock api module
|
||||||
vi.mock('@/lib/api', () => ({
|
vi.mock('@/lib/api', () => ({
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Card } from '@/components/ui/Card';
|
import { Card } from '@/components/ui/Card';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { TabsNavigation, type TabItem } from '@/components/ui/TabsNavigation';
|
import { TabsNavigation } from '@/components/ui/TabsNavigation';
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
@@ -30,7 +30,6 @@ import {
|
|||||||
import { OverviewTab } from '@/components/codexlens/OverviewTab';
|
import { OverviewTab } from '@/components/codexlens/OverviewTab';
|
||||||
import { SettingsTab } from '@/components/codexlens/SettingsTab';
|
import { SettingsTab } from '@/components/codexlens/SettingsTab';
|
||||||
import { AdvancedTab } from '@/components/codexlens/AdvancedTab';
|
import { AdvancedTab } from '@/components/codexlens/AdvancedTab';
|
||||||
import { GpuSelector } from '@/components/codexlens/GpuSelector';
|
|
||||||
import { ModelsTab } from '@/components/codexlens/ModelsTab';
|
import { ModelsTab } from '@/components/codexlens/ModelsTab';
|
||||||
import { SearchTab } from '@/components/codexlens/SearchTab';
|
import { SearchTab } from '@/components/codexlens/SearchTab';
|
||||||
import { SemanticInstallDialog } from '@/components/codexlens/SemanticInstallDialog';
|
import { SemanticInstallDialog } from '@/components/codexlens/SemanticInstallDialog';
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
// Tests for the issue discovery page with i18n
|
// Tests for the issue discovery page with i18n
|
||||||
|
|
||||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||||
import { render, screen, waitFor } from '@/test/i18n';
|
import { render, screen } from '@/test/i18n';
|
||||||
import { DiscoveryPage } from './DiscoveryPage';
|
import { DiscoveryPage } from './DiscoveryPage';
|
||||||
import { useWorkflowStore } from '@/stores/workflowStore';
|
import { useWorkflowStore } from '@/stores/workflowStore';
|
||||||
import type { DiscoverySession } from '@/lib/api';
|
import type { DiscoverySession } from '@/lib/api';
|
||||||
|
|||||||
@@ -13,14 +13,13 @@ import {
|
|||||||
XCircle,
|
XCircle,
|
||||||
BarChart3,
|
BarChart3,
|
||||||
Calendar,
|
Calendar,
|
||||||
Filter,
|
|
||||||
ListTree,
|
ListTree,
|
||||||
History,
|
History,
|
||||||
List,
|
List,
|
||||||
Monitor,
|
Monitor,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
||||||
import { TabsNavigation, type TabItem } from '@/components/ui/TabsNavigation';
|
import { TabsNavigation } from '@/components/ui/TabsNavigation';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { ExecutionMonitor } from './orchestrator/ExecutionMonitor';
|
import { ExecutionMonitor } from './orchestrator/ExecutionMonitor';
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
// ========================================
|
// ========================================
|
||||||
// Unified page for issues, queue, and discovery with tab navigation
|
// Unified page for issues, queue, and discovery with tab navigation
|
||||||
|
|
||||||
import { useState, useCallback, useRef } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { useSearchParams } from 'react-router-dom';
|
import { useSearchParams } from 'react-router-dom';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ export function IssueManagerPage() {
|
|||||||
try {
|
try {
|
||||||
const result = await pullIssuesFromGitHub({ state: 'open', limit: 100 });
|
const result = await pullIssuesFromGitHub({ state: 'open', limit: 100 });
|
||||||
await refetch();
|
await refetch();
|
||||||
toast.success(formatMessage({ id: 'issues.messages.githubSyncSuccess' }, result));
|
toast.success(formatMessage({ id: 'issues.messages.githubSyncSuccess' }, { ...result }));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('GitHub sync failed:', err);
|
console.error('GitHub sync failed:', err);
|
||||||
toast.error(formatMessage({ id: 'issues.messages.githubSyncError' }));
|
toast.error(formatMessage({ id: 'issues.messages.githubSyncError' }));
|
||||||
|
|||||||
@@ -15,11 +15,8 @@ import {
|
|||||||
ArrowLeft,
|
ArrowLeft,
|
||||||
FileEdit,
|
FileEdit,
|
||||||
Wrench,
|
Wrench,
|
||||||
Calendar,
|
|
||||||
Loader2,
|
|
||||||
XCircle,
|
XCircle,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
Clock,
|
|
||||||
Code,
|
Code,
|
||||||
Zap,
|
Zap,
|
||||||
ListTodo,
|
ListTodo,
|
||||||
@@ -31,7 +28,6 @@ import {
|
|||||||
Folder,
|
Folder,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
FileText,
|
FileText,
|
||||||
ChevronDown,
|
|
||||||
ChevronRight,
|
ChevronRight,
|
||||||
Ruler,
|
Ruler,
|
||||||
Stethoscope,
|
Stethoscope,
|
||||||
@@ -41,10 +37,8 @@ import { Flowchart } from '@/components/shared/Flowchart';
|
|||||||
import { Button } from '@/components/ui/Button';
|
import { Button } from '@/components/ui/Button';
|
||||||
import { Badge } from '@/components/ui/Badge';
|
import { Badge } from '@/components/ui/Badge';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
|
||||||
import { Tabs, TabsContent } from '@/components/ui/Tabs';
|
|
||||||
import { TabsNavigation } from '@/components/ui/TabsNavigation';
|
import { TabsNavigation } from '@/components/ui/TabsNavigation';
|
||||||
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/Collapsible';
|
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '@/components/ui/Collapsible';
|
||||||
import type { LiteTask } from '@/lib/api';
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Type Definitions
|
// Type Definitions
|
||||||
@@ -64,43 +58,6 @@ interface Exploration {
|
|||||||
content?: string;
|
content?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ExplorationAngle {
|
|
||||||
findings: string[];
|
|
||||||
recommendations: string[];
|
|
||||||
patterns: string[];
|
|
||||||
risks: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImplementationTask {
|
|
||||||
id: string;
|
|
||||||
title: string;
|
|
||||||
description?: string;
|
|
||||||
status?: string;
|
|
||||||
assignee?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Milestone {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
description?: string;
|
|
||||||
target_date?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DiscussionSolution {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
summary: string | { en: string; zh: string };
|
|
||||||
feasibility: number;
|
|
||||||
effort: 'low' | 'medium' | 'high';
|
|
||||||
risk: 'low' | 'medium' | 'high';
|
|
||||||
source_cli: string[];
|
|
||||||
implementation_plan: {
|
|
||||||
approach: string;
|
|
||||||
tasks: ImplementationTask[];
|
|
||||||
milestones: Milestone[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// Main Component
|
// Main Component
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|||||||
@@ -91,6 +91,24 @@ export {
|
|||||||
selectActiveTab,
|
selectActiveTab,
|
||||||
} from './viewerStore';
|
} from './viewerStore';
|
||||||
|
|
||||||
|
// Terminal Panel Store
|
||||||
|
export {
|
||||||
|
useTerminalPanelStore,
|
||||||
|
selectIsPanelOpen as selectIsTerminalPanelOpen,
|
||||||
|
selectActiveTerminalId,
|
||||||
|
selectPanelView,
|
||||||
|
selectTerminalOrder,
|
||||||
|
selectTerminalCount,
|
||||||
|
} from './terminalPanelStore';
|
||||||
|
|
||||||
|
// Terminal Panel Store Types
|
||||||
|
export type {
|
||||||
|
PanelView,
|
||||||
|
TerminalPanelState,
|
||||||
|
TerminalPanelActions,
|
||||||
|
TerminalPanelStore,
|
||||||
|
} from './terminalPanelStore';
|
||||||
|
|
||||||
// Re-export types for convenience
|
// Re-export types for convenience
|
||||||
export type {
|
export type {
|
||||||
// App Store Types
|
// App Store Types
|
||||||
|
|||||||
147
ccw/frontend/src/stores/terminalPanelStore.ts
Normal file
147
ccw/frontend/src/stores/terminalPanelStore.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
// ========================================
|
||||||
|
// Terminal Panel Store
|
||||||
|
// ========================================
|
||||||
|
// Zustand store for terminal panel UI state management.
|
||||||
|
// Manages panel visibility, active terminal, view mode, and terminal ordering.
|
||||||
|
// Separated from cliSessionStore to keep UI state independent of data state.
|
||||||
|
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { devtools } from 'zustand/middleware';
|
||||||
|
|
||||||
|
// ========== Types ==========
|
||||||
|
|
||||||
|
export type PanelView = 'terminal' | 'queue';
|
||||||
|
|
||||||
|
export interface TerminalPanelState {
|
||||||
|
/** Whether the bottom terminal panel is open */
|
||||||
|
isPanelOpen: boolean;
|
||||||
|
/** The sessionKey of the currently active terminal */
|
||||||
|
activeTerminalId: string | null;
|
||||||
|
/** Current panel view mode */
|
||||||
|
panelView: PanelView;
|
||||||
|
/** Ordered list of terminal sessionKeys (tab order) */
|
||||||
|
terminalOrder: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TerminalPanelActions {
|
||||||
|
/** Open panel and activate the given terminal; adds it to order if new */
|
||||||
|
openTerminal: (sessionKey: string) => void;
|
||||||
|
/** Close the terminal panel (keeps terminal order intact) */
|
||||||
|
closePanel: () => void;
|
||||||
|
/** Switch active terminal without opening/closing */
|
||||||
|
setActiveTerminal: (sessionKey: string) => void;
|
||||||
|
/** Switch panel view between 'terminal' and 'queue' */
|
||||||
|
setPanelView: (view: PanelView) => void;
|
||||||
|
/** Add a terminal to the order list (no-op if already present) */
|
||||||
|
addTerminal: (sessionKey: string) => void;
|
||||||
|
/** Remove a terminal from the order list and adjust active if needed */
|
||||||
|
removeTerminal: (sessionKey: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TerminalPanelStore = TerminalPanelState & TerminalPanelActions;
|
||||||
|
|
||||||
|
// ========== Initial State ==========
|
||||||
|
|
||||||
|
const initialState: TerminalPanelState = {
|
||||||
|
isPanelOpen: false,
|
||||||
|
activeTerminalId: null,
|
||||||
|
panelView: 'terminal',
|
||||||
|
terminalOrder: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== Store ==========
|
||||||
|
|
||||||
|
export const useTerminalPanelStore = create<TerminalPanelStore>()(
|
||||||
|
devtools(
|
||||||
|
(set, get) => ({
|
||||||
|
...initialState,
|
||||||
|
|
||||||
|
// ========== Panel Lifecycle ==========
|
||||||
|
|
||||||
|
openTerminal: (sessionKey: string) => {
|
||||||
|
const { terminalOrder } = get();
|
||||||
|
const nextOrder = terminalOrder.includes(sessionKey)
|
||||||
|
? terminalOrder
|
||||||
|
: [...terminalOrder, sessionKey];
|
||||||
|
|
||||||
|
set(
|
||||||
|
{
|
||||||
|
isPanelOpen: true,
|
||||||
|
activeTerminalId: sessionKey,
|
||||||
|
panelView: 'terminal',
|
||||||
|
terminalOrder: nextOrder,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
'openTerminal'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
closePanel: () => {
|
||||||
|
set({ isPanelOpen: false }, false, 'closePanel');
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== Terminal Selection ==========
|
||||||
|
|
||||||
|
setActiveTerminal: (sessionKey: string) => {
|
||||||
|
set({ activeTerminalId: sessionKey }, false, 'setActiveTerminal');
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== View Mode ==========
|
||||||
|
|
||||||
|
setPanelView: (view: PanelView) => {
|
||||||
|
set({ panelView: view }, false, 'setPanelView');
|
||||||
|
},
|
||||||
|
|
||||||
|
// ========== Terminal Order Management ==========
|
||||||
|
|
||||||
|
addTerminal: (sessionKey: string) => {
|
||||||
|
const { terminalOrder } = get();
|
||||||
|
if (terminalOrder.includes(sessionKey)) return;
|
||||||
|
|
||||||
|
set(
|
||||||
|
{ terminalOrder: [...terminalOrder, sessionKey] },
|
||||||
|
false,
|
||||||
|
'addTerminal'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
removeTerminal: (sessionKey: string) => {
|
||||||
|
const { terminalOrder, activeTerminalId } = get();
|
||||||
|
const nextOrder = terminalOrder.filter((key) => key !== sessionKey);
|
||||||
|
|
||||||
|
// If removed terminal was active, activate the previous or next neighbor
|
||||||
|
let nextActive = activeTerminalId;
|
||||||
|
if (activeTerminalId === sessionKey) {
|
||||||
|
const removedIndex = terminalOrder.indexOf(sessionKey);
|
||||||
|
if (nextOrder.length === 0) {
|
||||||
|
nextActive = null;
|
||||||
|
} else if (removedIndex >= nextOrder.length) {
|
||||||
|
nextActive = nextOrder[nextOrder.length - 1];
|
||||||
|
} else {
|
||||||
|
nextActive = nextOrder[removedIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set(
|
||||||
|
{
|
||||||
|
terminalOrder: nextOrder,
|
||||||
|
activeTerminalId: nextActive,
|
||||||
|
// Auto-close panel when no terminals remain
|
||||||
|
isPanelOpen: nextOrder.length > 0 ? get().isPanelOpen : false,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
'removeTerminal'
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{ name: 'TerminalPanelStore' }
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ========== Selectors ==========
|
||||||
|
|
||||||
|
export const selectIsPanelOpen = (state: TerminalPanelStore) => state.isPanelOpen;
|
||||||
|
export const selectActiveTerminalId = (state: TerminalPanelStore) => state.activeTerminalId;
|
||||||
|
export const selectPanelView = (state: TerminalPanelStore) => state.panelView;
|
||||||
|
export const selectTerminalOrder = (state: TerminalPanelStore) => state.terminalOrder;
|
||||||
|
export const selectTerminalCount = (state: TerminalPanelStore) => state.terminalOrder.length;
|
||||||
@@ -206,6 +206,9 @@ export interface ExecutionStoreActions {
|
|||||||
completeToolCall: (nodeId: string, callId: string, result: { status: ToolCallExecution['status']; exitCode?: number; error?: string; result?: unknown }) => void;
|
completeToolCall: (nodeId: string, callId: string, result: { status: ToolCallExecution['status']; exitCode?: number; error?: string; result?: unknown }) => void;
|
||||||
toggleToolCallExpanded: (nodeId: string, callId: string) => void;
|
toggleToolCallExpanded: (nodeId: string, callId: string) => void;
|
||||||
|
|
||||||
|
// Tool call getters
|
||||||
|
getToolCallsForNode: (nodeId: string) => ToolCallExecution[];
|
||||||
|
|
||||||
// Node selection (new)
|
// Node selection (new)
|
||||||
selectNode: (nodeId: string | null) => void;
|
selectNode: (nodeId: string | null) => void;
|
||||||
|
|
||||||
|
|||||||
281
ccw/frontend/tsc-errors.txt
Normal file
281
ccw/frontend/tsc-errors.txt
Normal file
@@ -0,0 +1,281 @@
|
|||||||
|
src/components/api-settings/CliSettingsModal.tsx(40,10): error TS6133: 'safeStringifyConfig' is declared but its value is never read.
|
||||||
|
src/components/issue/queue/ExecutionGroup.test.tsx(130,13): error TS6133: 'numberElements' is declared but its value is never read.
|
||||||
|
src/components/issue/queue/QueueCard.test.tsx(20,13): error TS2322: Type 'string' is not assignable to type 'QueueItem'.
|
||||||
|
src/components/issue/queue/QueueCard.test.tsx(20,22): error TS2322: Type 'string' is not assignable to type 'QueueItem'.
|
||||||
|
src/components/issue/queue/QueueCard.test.tsx(21,17): error TS2322: Type 'string' is not assignable to type 'QueueItem'.
|
||||||
|
src/components/layout/AppShell.tsx(130,9): error TS6133: 'handleMenuClick' is declared but its value is never read.
|
||||||
|
src/components/layout/Header.test.tsx(45,22): error TS2322: Type '{ onMenuClick: Mock<Procedure>; }' is not assignable to type 'IntrinsicAttributes & HeaderProps'.
|
||||||
|
Property 'onMenuClick' does not exist on type 'IntrinsicAttributes & HeaderProps'.
|
||||||
|
src/components/mcp/ConfigTypeToggle.tsx(18,1): error TS6133: 'Button' is declared but its value is never read.
|
||||||
|
src/components/mcp/ConfigTypeToggle.tsx(19,1): error TS6133: 'Badge' is declared but its value is never read.
|
||||||
|
src/components/mcp/CrossCliSyncPanel.tsx(33,6): error TS6196: 'CopyDirection' is declared but never used.
|
||||||
|
src/components/mcp/RecommendedMcpSection.tsx(13,3): error TS6133: 'Check' is declared but its value is never read.
|
||||||
|
src/components/notification/NotificationPanel.tsx(24,12): error TS6133: 'ImageIcon' is declared but its value is never read.
|
||||||
|
src/components/notification/NotificationPanel.tsx(70,10): error TS6133: 'formatDetails' is declared but its value is never read.
|
||||||
|
src/components/notification/NotificationPanel.tsx(721,9): error TS6133: 'formatMessage' is declared but its value is never read.
|
||||||
|
src/components/orchestrator/ToolCallCard.tsx(64,15): error TS2304: Cannot find name 'FileText'.
|
||||||
|
src/components/orchestrator/ToolCallsTimeline.tsx(6,8): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/JsonCard.tsx(13,3): error TS6133: 'ChevronRight' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/JsonCard.tsx(17,1): error TS6133: 'Button' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/JsonCard.tsx(18,1): error TS6133: 'Badge' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/JsonCard.tsx(97,3): error TS6133: 'timestamp' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/JsonCard.tsx(98,3): error TS6133: 'onCopy' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/JsonField.tsx(12,10): error TS6133: 'copied' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/components/OutputLine.tsx(8,1): error TS6133: 'cn' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/messages/AssistantMessage.tsx(94,9): error TS6133: 'formatMessage' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/messages/ErrorMessage.tsx(22,3): error TS6133: 'timestamp' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitor/messages/UserMessage.tsx(21,3): error TS6133: 'timestamp' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitorLegacy.tsx(157,34): error TS6133: 'onCopy' is declared but its value is never read.
|
||||||
|
src/components/shared/CliStreamMonitorLegacy.tsx(361,9): error TS6133: 'scrollToBottom' is declared but its value is never read.
|
||||||
|
src/components/shared/InsightDetailPanel.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/components/shared/InsightDetailPanel.tsx(136,33): error TS6133: 'locale' is declared but its value is never read.
|
||||||
|
src/components/shared/InsightDetailPanel.tsx(137,9): error TS6133: 'formatMessage' is declared but its value is never read.
|
||||||
|
src/components/shared/InsightDetailPanel.tsx(180,39): error TS6133: 'locale' is declared but its value is never read.
|
||||||
|
src/components/shared/InsightsPanel.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/components/shared/LogBlock/LogBlock.tsx(5,8): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/components/shared/LogBlock/LogBlockList.tsx(6,33): error TS6133: 'useMemo' is declared but its value is never read.
|
||||||
|
src/components/shared/PromptStats.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/components/shared/TickerMarquee.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/components/shared/TickerMarquee.tsx(13,3): error TS6133: 'XCircle' is declared but its value is never read.
|
||||||
|
src/components/shared/VersionCheck.tsx(2,23): error TS1149: File name 'D:/Claude_dms3/ccw/frontend/src/components/ui/badge.tsx' differs from already included file name 'D:/Claude_dms3/ccw/frontend/src/components/ui/Badge.tsx' only in casing.
|
||||||
|
The file is in the program because:
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/layout/Header.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/CliStreamMonitorLegacy.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/LogBlock/LogBlock.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/CliStreamMonitor/components/JsonCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/notification/NotificationPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/dashboard/widgets/RecentSessionsWidget.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/SessionCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/SessionsPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/FixSessionPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/ProjectOverviewPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/tasks/TaskStatusDropdown.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/session-detail/ContextTab.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/context/FieldRenderer.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/context/ExplorationsSection.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/context/AssetsCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/context/DependenciesCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/context/TestContextCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/session-detail/context/ConflictDetectionCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/session-detail/ConflictTab.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/session-detail/ReviewTab.tsx'
|
||||||
|
Imported via '../ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/TaskDrawer.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/SessionDetailPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/ConversationCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/CliStreamPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/NativeSessionPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/orchestrator/InlineTemplatePanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/orchestrator/TemplateLibrary.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/orchestrator/ExecutionMonitor.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/LoopMonitorPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/KanbanBoard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/hub/IssuesPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/IssueCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/hub/IssueDrawer.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/hub/QueuePanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/queue/QueueCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/queue/ExecutionGroup.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/queue/QueueBoard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/queue/SolutionDrawer.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/hub/DiscoveryPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/discovery/DiscoveryCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/discovery/DiscoveryDetail.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/discovery/FindingList.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/hub/ObservabilityPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/QueuePage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/DiscoveryPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/SkillsManagerPage.tsx'
|
||||||
|
Imported via "./Badge" from file 'D:/Claude_dms3/ccw/frontend/src/components/ui/index.ts'
|
||||||
|
Imported via "./Badge" from file 'D:/Claude_dms3/ccw/frontend/src/components/ui/index.ts'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/SkillCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/SkillDetailPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/RuleCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/PromptCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/QualityBadge.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/GraphToolbar.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/GraphSidebar.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/InsightsPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/IndexManager.tsx'
|
||||||
|
Imported via '../ui/badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/VersionCheck.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/ConfigSync.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/CommandsManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/commands/CommandGroupAccordion.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/MemoryPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/SettingsPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/HookManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/hook/HookCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/hook/EventGroup.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/hook/HookQuickTemplates.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/hook/HookWizard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/LiteTasksPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/lite-tasks/LiteContextContent.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/ReviewSessionPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/McpManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/McpServerDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/ConfigTypeToggle.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/CodexMcpEditableCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/CcwToolsMcpCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/McpTemplatesSection.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/RecommendedMcpSection.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/WindowsCompatibilityWarning.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/CrossCliSyncPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/AllProjectsTable.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/OtherProjectsSection.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/EndpointsPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/InstallationsPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/RulesManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/PromptHistoryPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/FileWatcherCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/AdvancedTab.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/GpuSelector.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/ModelsTab.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/ModelCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/ProviderList.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/EndpointList.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/ModelPoolList.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/CliSettingsList.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/ManageModelsModal.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/CliSessionSharePage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/IssueManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/team/TeamHeader.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/team/TeamMembersPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/team/TeamMessageFeed.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/dashboard/widgets/TaskMarqueeWidget.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/dashboard/widgets/WorkflowStatusProgressWidget.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/CodexMcpCard.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/CrossCliCopyButton.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/EnterpriseMcpBadge.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/InstallCommandDialog.tsx'
|
||||||
|
Matched by include pattern 'src' in 'D:/Claude_dms3/ccw/frontend/tsconfig.json'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/ExecutionMonitorPage.tsx'
|
||||||
|
Imported via '@/components/ui/Badge' from file 'D:/Claude_dms3/ccw/frontend/src/pages/LiteTaskDetailPage.tsx'
|
||||||
|
src/components/shared/VersionCheckModal.tsx(6,8): error TS1149: File name 'D:/Claude_dms3/ccw/frontend/src/components/ui/dialog.tsx' differs from already included file name 'D:/Claude_dms3/ccw/frontend/src/components/ui/Dialog.tsx' only in casing.
|
||||||
|
The file is in the program because:
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/workspace/WorkspaceSelector.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/a2ui/AskQuestionDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/a2ui/A2UIPopupCard.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/SessionsPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/MarkdownModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/CliStreamPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/NativeSessionPanel.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/HistoryPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/orchestrator/TemplateLibrary.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/LoopMonitorPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/issue/queue/QueueActions.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/IssueHubPage.tsx'
|
||||||
|
Imported via "./Dialog" from file 'D:/Claude_dms3/ccw/frontend/src/components/ui/index.ts'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/SkillCreateDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/RuleDialog.tsx'
|
||||||
|
Imported via '../ui/dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/VersionCheckModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/shared/ConfigSyncModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/MemoryPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/hook/HookFormDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/hook/HookWizard.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/McpServerDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/McpTemplatesSection.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/RecommendedMcpWizard.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/cli-endpoints/CliEndpointFormDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/RulesManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/PromptHistoryPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/SemanticInstallDialog.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/codexlens/InstallProgressOverlay.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/ProviderModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/EndpointModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/ModelPoolModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/CliSettingsModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/MultiKeySettingsModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/api-settings/ManageModelsModal.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/cli-viewer/ExecutionPicker.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/pages/IssueManagerPage.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/CrossCliCopyButton.tsx'
|
||||||
|
Imported via '@/components/ui/Dialog' from file 'D:/Claude_dms3/ccw/frontend/src/components/mcp/InstallCommandDialog.tsx'
|
||||||
|
Matched by include pattern 'src' in 'D:/Claude_dms3/ccw/frontend/tsconfig.json'
|
||||||
|
src/components/team/TeamHeader.tsx(7,32): error TS6133: 'Clock' is declared but its value is never read.
|
||||||
|
src/components/team/TeamMessageFeed.tsx(10,1): error TS6133: 'Badge' is declared but its value is never read.
|
||||||
|
src/components/team/TeamMessageFeed.tsx(20,28): error TS6196: 'TeamMessageType' is declared but never used.
|
||||||
|
src/components/team/TeamPipeline.tsx(107,10): error TS6133: 'ForkArrow' is declared but its value is never read.
|
||||||
|
src/components/ui/ContextAssembler.tsx(9,16): error TS6133: 'Plus' is declared but its value is never read.
|
||||||
|
src/components/ui/MultiNodeSelector.tsx(22,47): error TS6133: 'placeholder' is declared but its value is never read.
|
||||||
|
src/hooks/useActiveCliExecutions.ts(111,9): error TS6133: 'markExecutionClosedByUser' is declared but its value is never read.
|
||||||
|
src/hooks/useApiSettings.ts(47,8): error TS6133: 'GlobalCacheSettings' is declared but its value is never read.
|
||||||
|
src/hooks/useApiSettings.ts(50,8): error TS6133: 'DiscoveredProvider' is declared but its value is never read.
|
||||||
|
src/hooks/useGraphData.ts(12,8): error TS6133: 'GraphImpactRequest' is declared but its value is never read.
|
||||||
|
src/hooks/useGraphData.ts(13,8): error TS6133: 'GraphImpactResponse' is declared but its value is never read.
|
||||||
|
src/hooks/useGraphData.ts(17,3): error TS6196: 'GraphNode' is declared but never used.
|
||||||
|
src/hooks/useGraphData.ts(18,3): error TS6196: 'GraphEdge' is declared but never used.
|
||||||
|
src/hooks/useGraphData.ts(20,3): error TS6196: 'GraphMetadata' is declared but never used.
|
||||||
|
src/hooks/useGraphData.ts(135,42): error TS6133: 'node' is declared but its value is never read.
|
||||||
|
src/hooks/useGraphData.ts(242,5): error TS6133: 'edgeTypes' is declared but its value is never read.
|
||||||
|
src/hooks/useIssues.test.tsx(11,3): error TS6133: 'useIssueMutations' is declared but its value is never read.
|
||||||
|
src/hooks/useIssues.ts(28,8): error TS6133: 'IssuesResponse' is declared but its value is never read.
|
||||||
|
src/hooks/useMcpServers.ts(27,8): error TS6133: 'AllProjectsResponse' is declared but its value is never read.
|
||||||
|
src/hooks/useMcpServers.ts(442,30): error TS6133: 'context' is declared but its value is never read.
|
||||||
|
src/hooks/useSessions.ts(171,17): error TS6133: 'newSession' is declared but its value is never read.
|
||||||
|
src/hooks/useSkills.ts(12,8): error TS6133: 'SkillsResponse' is declared but its value is never read.
|
||||||
|
src/hooks/useWebSocket.ts(54,36): error TS2339: Property 'getToolCallsForNode' does not exist on type 'ExecutionStore'.
|
||||||
|
src/hooks/useWebSocket.ts(245,52): error TS7006: Parameter 'c' implicitly has an 'any' type.
|
||||||
|
src/hooks/useWorkflowStatusCounts.ts(6,20): error TS6133: 'useQueryClient' is declared but its value is never read.
|
||||||
|
src/hooks/useWorkflowStatusCounts.ts(8,1): error TS6133: 'workspaceQueryKeys' is declared but its value is never read.
|
||||||
|
src/lib/api.mcp.test.ts(77,41): error TS6133: 'init' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/__tests__/A2UIComponentRegistry.test.ts(264,47): error TS6133: 'actionId' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/__tests__/A2UIComponentRegistry.test.ts(264,57): error TS6133: 'params' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/__tests__/A2UIParser.test.ts(8,10): error TS6133: 'A2UIParser' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/__tests__/A2UIParser.test.ts(9,1): error TS6192: All imports in import declaration are unused.
|
||||||
|
src/packages/a2ui-runtime/__tests__/components.test.tsx(7,35): error TS6133: 'within' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/__tests__/components.test.tsx(656,49): error TS6133: 'actionId' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/__tests__/components.test.tsx(656,59): error TS6133: 'params' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx(6,8): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx(7,30): error TS6196: 'SurfaceComponent' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/A2UIRenderer.tsx(66,9): error TS6133: 'contextValue' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIButton.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIButton.tsx(13,11): error TS6196: 'A2UIButtonRendererProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIButton.tsx(24,60): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICard.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICard.tsx(12,11): error TS6196: 'A2UICardProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICard.tsx(23,58): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICard.tsx(23,65): error TS6133: 'onAction' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICheckbox.tsx(6,8): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICheckbox.tsx(13,11): error TS6196: 'A2UICheckboxProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICheckbox.tsx(24,62): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICLIOutput.tsx(104,63): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UICLIOutput.tsx(104,70): error TS6133: 'onAction' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIDateTimeInput.tsx(8,10): error TS6133: 'resolveLiteralOrBinding' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIDateTimeInput.tsx(33,52): error TS6133: 'includeTime' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIDateTimeInput.tsx(46,67): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIProgress.tsx(6,1): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIProgress.tsx(12,11): error TS6196: 'A2UIProgressProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIProgress.tsx(23,62): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIProgress.tsx(23,69): error TS6133: 'onAction' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIRadioGroup.tsx(7,8): error TS6133: 'React' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIRadioGroup.tsx(14,11): error TS6196: 'A2UIRadioGroupProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIRadioGroup.tsx(25,64): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIText.tsx(11,11): error TS6196: 'A2UITextProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIText.tsx(22,58): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UIText.tsx(22,65): error TS6133: 'onAction' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UITextArea.tsx(12,11): error TS6196: 'A2UITextAreaProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UITextArea.tsx(23,62): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UITextField.tsx(12,11): error TS6196: 'A2UITextFieldProps' is declared but never used.
|
||||||
|
src/packages/a2ui-runtime/renderer/components/A2UITextField.tsx(23,63): error TS6133: 'state' is declared but its value is never read.
|
||||||
|
src/pages/CliViewerPage.tsx(191,9): error TS6133: 'isSyncing' is declared but its value is never read.
|
||||||
|
src/pages/CodexLensManagerPage.test.tsx(10,1): error TS6133: 'api' is declared but its value is never read.
|
||||||
|
src/pages/CodexLensManagerPage.tsx(18,31): error TS6133: 'TabItem' is declared but its value is never read.
|
||||||
|
src/pages/CodexLensManagerPage.tsx(33,1): error TS6133: 'GpuSelector' is declared but its value is never read.
|
||||||
|
src/pages/DiscoveryPage.test.tsx(7,26): error TS6133: 'waitFor' is declared but its value is never read.
|
||||||
|
src/pages/ExecutionMonitorPage.tsx(16,3): error TS6133: 'Filter' is declared but its value is never read.
|
||||||
|
src/pages/ExecutionMonitorPage.tsx(23,31): error TS6133: 'TabItem' is declared but its value is never read.
|
||||||
|
src/pages/IssueHubPage.tsx(6,33): error TS6133: 'useRef' is declared but its value is never read.
|
||||||
|
src/pages/IssueManagerPage.tsx(389,80): error TS2769: No overload matches this call.
|
||||||
|
Overload 1 of 2, '(this: void, descriptor: MessageDescriptor, values?: Record<string, PrimitiveType | FormatXMLElementFn<string, string>> | undefined, opts?: Options | undefined): string', gave the following error.
|
||||||
|
Argument of type 'GitHubPullResponse' is not assignable to parameter of type 'Record<string, PrimitiveType | FormatXMLElementFn<string, string>>'.
|
||||||
|
Index signature for type 'string' is missing in type 'GitHubPullResponse'.
|
||||||
|
Overload 2 of 2, '(this: void, descriptor: MessageDescriptor, values?: Record<string, string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | ... 5 more ... | undefined> | undefined, opts?: Options | undefined): ReactNode[]', gave the following error.
|
||||||
|
Argument of type 'GitHubPullResponse' is not assignable to parameter of type 'Record<string, string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ... 4 more ... | undefined>'.
|
||||||
|
Index signature for type 'string' is missing in type 'GitHubPullResponse'.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(18,3): error TS6133: 'Calendar' is declared but its value is never read.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(19,3): error TS6133: 'Loader2' is declared but its value is never read.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(22,3): error TS6133: 'Clock' is declared but its value is never read.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(34,3): error TS6133: 'ChevronDown' is declared but its value is never read.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(44,1): error TS6192: All imports in import declaration are unused.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(47,1): error TS6133: 'LiteTask' is declared but its value is never read.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(67,11): error TS6196: 'ExplorationAngle' is declared but never used.
|
||||||
|
src/pages/LiteTaskDetailPage.tsx(89,11): error TS6196: 'DiscussionSolution' is declared but never used.
|
||||||
Reference in New Issue
Block a user