feat: clickable terminal events, node inspector drawer, stop animation on complete, vite 8
1. Terminal: remove inline prompts/full text; show short summary per event; click any event to open detail drawer with full request/response/model/metrics 2. Fix node "thinking" animation: shimmer only when status=running; on_chat_model_end (result) transitions node to completed, animation stops 3. Link nodes to events: clicking a graph node opens the drawer showing all events for that node (prompts, tool calls, results) 4. Upgrade Vite 5→8.0.1, @vitejs/plugin-react→5.2.0; update tsconfig moduleResolution to "bundler" for Vite 8 compat Co-authored-by: aguzererler <6199053+aguzererler@users.noreply.github.com> Agent-Logs-Url: https://github.com/aguzererler/TradingAgents/sessions/93c31c35-9509-4254-96fd-6f47aad07927
This commit is contained in:
parent
06e913f1ba
commit
cf2df83c38
File diff suppressed because it is too large
Load Diff
|
|
@ -25,7 +25,7 @@
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||||
"@typescript-eslint/parser": "^6.21.0",
|
"@typescript-eslint/parser": "^6.21.0",
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^5.2.0",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
|
@ -33,6 +33,6 @@
|
||||||
"postcss": "^8.4.47",
|
"postcss": "^8.4.47",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.0",
|
||||||
"typescript": "^5.6.0",
|
"typescript": "^5.6.0",
|
||||||
"vite": "^5.4.0"
|
"vite": "^8.0.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useRef, useEffect } from 'react';
|
import React, { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
|
|
@ -13,10 +13,13 @@ import {
|
||||||
DrawerContent,
|
DrawerContent,
|
||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerBody,
|
DrawerBody,
|
||||||
|
DrawerCloseButton,
|
||||||
Divider,
|
Divider,
|
||||||
Tag,
|
Tag,
|
||||||
|
Code,
|
||||||
|
Badge,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { LayoutDashboard, Wallet, Settings, Play, Terminal as TerminalIcon, ChevronRight } from 'lucide-react';
|
import { LayoutDashboard, Wallet, Settings, Play, Terminal as TerminalIcon, ChevronRight, Eye } from 'lucide-react';
|
||||||
import { MetricHeader } from './components/MetricHeader';
|
import { MetricHeader } from './components/MetricHeader';
|
||||||
import { AgentGraph } from './components/AgentGraph';
|
import { AgentGraph } from './components/AgentGraph';
|
||||||
import { useAgentStream, AgentEvent } from './hooks/useAgentStream';
|
import { useAgentStream, AgentEvent } from './hooks/useAgentStream';
|
||||||
|
|
@ -45,13 +48,101 @@ const eventLabel = (type: AgentEvent['type']): string => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Short summary for terminal — no inline prompts, just agent + type. */
|
||||||
|
const eventSummary = (evt: AgentEvent): string => {
|
||||||
|
switch (evt.type) {
|
||||||
|
case 'thought': return `Thinking… (${evt.metrics?.model || 'LLM'})`;
|
||||||
|
case 'tool': return evt.message.startsWith('✓') ? 'Tool result received' : `Tool call: ${evt.message.replace(/^▶ Tool: /, '').split(' | ')[0]}`;
|
||||||
|
case 'result': return 'Completed';
|
||||||
|
case 'log': return evt.message;
|
||||||
|
default: return evt.type;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Detail drawer for a single event ─────────────────────────────────
|
||||||
|
const EventDetail: React.FC<{ event: AgentEvent }> = ({ event }) => (
|
||||||
|
<VStack align="stretch" spacing={4}>
|
||||||
|
<HStack>
|
||||||
|
<Badge colorScheme="cyan">{event.type.toUpperCase()}</Badge>
|
||||||
|
<Badge variant="outline">{event.agent}</Badge>
|
||||||
|
<Text fontSize="xs" color="whiteAlpha.400">{event.timestamp}</Text>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{event.metrics?.model && event.metrics.model !== 'unknown' && (
|
||||||
|
<Box>
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="whiteAlpha.600" mb={1}>Model</Text>
|
||||||
|
<Code colorScheme="blue" fontSize="sm">{event.metrics.model}</Code>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{event.metrics && (event.metrics.tokens_in != null || event.metrics.latency_ms != null) && (
|
||||||
|
<Box>
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="whiteAlpha.600" mb={1}>Metrics</Text>
|
||||||
|
<HStack spacing={4} fontSize="sm">
|
||||||
|
{event.metrics.tokens_in != null && (
|
||||||
|
<Text>Tokens: <Code>{event.metrics.tokens_in}</Code> in / <Code>{event.metrics.tokens_out}</Code> out</Text>
|
||||||
|
)}
|
||||||
|
{event.metrics.latency_ms != null && event.metrics.latency_ms > 0 && (
|
||||||
|
<Text>Latency: <Code>{event.metrics.latency_ms}ms</Code></Text>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="whiteAlpha.600" mb={1}>
|
||||||
|
{event.type === 'thought' ? 'Request / Prompt' : event.type === 'result' ? 'Response' : 'Message'}
|
||||||
|
</Text>
|
||||||
|
<Box bg="blackAlpha.500" p={3} borderRadius="md" border="1px solid" borderColor="whiteAlpha.100" maxH="300px" overflowY="auto">
|
||||||
|
<Text fontSize="xs" fontFamily="mono" whiteSpace="pre-wrap" wordBreak="break-word" color="whiteAlpha.900">
|
||||||
|
{event.message}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{event.node_id && (
|
||||||
|
<Box>
|
||||||
|
<Text fontSize="xs" fontWeight="bold" color="whiteAlpha.600" mb={1}>Node ID</Text>
|
||||||
|
<Code fontSize="xs">{event.node_id}</Code>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
|
||||||
|
// ─── Detail drawer showing all events for a given graph node ──────────
|
||||||
|
const NodeEventsDetail: React.FC<{ nodeId: string; events: AgentEvent[] }> = ({ nodeId, events }) => {
|
||||||
|
const nodeEvents = useMemo(
|
||||||
|
() => events.filter((e) => e.node_id === nodeId),
|
||||||
|
[events, nodeId],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (nodeEvents.length === 0) {
|
||||||
|
return <Text color="whiteAlpha.500" fontSize="sm">No events recorded for this node yet.</Text>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VStack align="stretch" spacing={4}>
|
||||||
|
{nodeEvents.map((evt) => (
|
||||||
|
<Box key={evt.id} bg="whiteAlpha.50" p={3} borderRadius="md" border="1px solid" borderColor="whiteAlpha.100">
|
||||||
|
<EventDetail event={evt} />
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</VStack>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export const Dashboard: React.FC = () => {
|
export const Dashboard: React.FC = () => {
|
||||||
const [activeRunId, setActiveRunId] = useState<string | null>(null);
|
const [activeRunId, setActiveRunId] = useState<string | null>(null);
|
||||||
const [isTriggering, setIsTriggering] = useState(false);
|
const [isTriggering, setIsTriggering] = useState(false);
|
||||||
const [portfolioId, setPortfolioId] = useState<string>("main_portfolio");
|
const [portfolioId, setPortfolioId] = useState<string>("main_portfolio");
|
||||||
const { events, status, clearEvents } = useAgentStream(activeRunId);
|
const { events, status, clearEvents } = useAgentStream(activeRunId);
|
||||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||||
const [selectedNode, setSelectedNode] = useState<any>(null);
|
|
||||||
|
// What's shown in the drawer: either a single event or all events for a node
|
||||||
|
const [drawerMode, setDrawerMode] = useState<'event' | 'node'>('event');
|
||||||
|
const [selectedEvent, setSelectedEvent] = useState<AgentEvent | null>(null);
|
||||||
|
const [selectedNodeId, setSelectedNodeId] = useState<string | null>(null);
|
||||||
|
|
||||||
// Auto-scroll the terminal to the bottom as new events arrive
|
// Auto-scroll the terminal to the bottom as new events arrive
|
||||||
const terminalEndRef = useRef<HTMLDivElement>(null);
|
const terminalEndRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
|
@ -77,6 +168,27 @@ export const Dashboard: React.FC = () => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Open the drawer for a single event (terminal click). */
|
||||||
|
const openEventDetail = useCallback((evt: AgentEvent) => {
|
||||||
|
setDrawerMode('event');
|
||||||
|
setSelectedEvent(evt);
|
||||||
|
setSelectedNodeId(null);
|
||||||
|
onOpen();
|
||||||
|
}, [onOpen]);
|
||||||
|
|
||||||
|
/** Open the drawer showing all events for a graph node (node click). */
|
||||||
|
const openNodeDetail = useCallback((nodeId: string) => {
|
||||||
|
setDrawerMode('node');
|
||||||
|
setSelectedNodeId(nodeId);
|
||||||
|
setSelectedEvent(null);
|
||||||
|
onOpen();
|
||||||
|
}, [onOpen]);
|
||||||
|
|
||||||
|
// Derive a readable drawer title
|
||||||
|
const drawerTitle = drawerMode === 'event'
|
||||||
|
? `Event: ${selectedEvent?.agent ?? ''} — ${selectedEvent?.type ?? ''}`
|
||||||
|
: `Node: ${selectedNodeId ?? ''}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex h="100vh" bg="slate.950" color="white" overflow="hidden">
|
<Flex h="100vh" bg="slate.950" color="white" overflow="hidden">
|
||||||
{/* Sidebar */}
|
{/* Sidebar */}
|
||||||
|
|
@ -96,7 +208,7 @@ export const Dashboard: React.FC = () => {
|
||||||
<Flex flex="1" overflow="hidden">
|
<Flex flex="1" overflow="hidden">
|
||||||
{/* Left Side: Graph Area */}
|
{/* Left Side: Graph Area */}
|
||||||
<Box flex="1" position="relative" borderRight="1px solid" borderColor="whiteAlpha.100">
|
<Box flex="1" position="relative" borderRight="1px solid" borderColor="whiteAlpha.100">
|
||||||
<AgentGraph events={events} />
|
<AgentGraph events={events} onNodeClick={openNodeDetail} />
|
||||||
|
|
||||||
{/* Floating Control Panel */}
|
{/* Floating Control Panel */}
|
||||||
<HStack position="absolute" top={4} left={4} bg="blackAlpha.800" p={2} borderRadius="lg" backdropFilter="blur(10px)" border="1px solid" borderColor="whiteAlpha.200" spacing={3}>
|
<HStack position="absolute" top={4} left={4} bg="blackAlpha.800" p={2} borderRadius="lg" backdropFilter="blur(10px)" border="1px solid" borderColor="whiteAlpha.200" spacing={3}>
|
||||||
|
|
@ -131,23 +243,29 @@ export const Dashboard: React.FC = () => {
|
||||||
'&::-webkit-scrollbar-thumb': { background: 'whiteAlpha.300' }
|
'&::-webkit-scrollbar-thumb': { background: 'whiteAlpha.300' }
|
||||||
}}>
|
}}>
|
||||||
{events.map((evt) => (
|
{events.map((evt) => (
|
||||||
<Box key={evt.id} mb={3} fontSize="xs" fontFamily="mono">
|
<Box
|
||||||
<Flex gap={2} align="flex-start">
|
key={evt.id}
|
||||||
<Text color="whiteAlpha.400" minW="60px" flexShrink={0}>[{evt.timestamp}]</Text>
|
mb={2}
|
||||||
|
fontSize="xs"
|
||||||
|
fontFamily="mono"
|
||||||
|
px={2}
|
||||||
|
py={1}
|
||||||
|
borderRadius="md"
|
||||||
|
cursor="pointer"
|
||||||
|
_hover={{ bg: 'whiteAlpha.100' }}
|
||||||
|
onClick={() => openEventDetail(evt)}
|
||||||
|
transition="background 0.15s"
|
||||||
|
>
|
||||||
|
<Flex gap={2} align="center">
|
||||||
|
<Text color="whiteAlpha.400" minW="52px" flexShrink={0}>[{evt.timestamp}]</Text>
|
||||||
<Text flexShrink={0}>{eventLabel(evt.type)}</Text>
|
<Text flexShrink={0}>{eventLabel(evt.type)}</Text>
|
||||||
<Text color={eventColor(evt.type)} fontWeight="bold" flexShrink={0}>
|
<Text color={eventColor(evt.type)} fontWeight="bold" flexShrink={0}>
|
||||||
{evt.agent}
|
{evt.agent}
|
||||||
</Text>
|
</Text>
|
||||||
<ChevronRight size={12} style={{ marginTop: 2, flexShrink: 0 }} />
|
<ChevronRight size={10} style={{ flexShrink: 0, opacity: 0.4 }} />
|
||||||
<Text color="whiteAlpha.800" wordBreak="break-word">{evt.message}</Text>
|
<Text color="whiteAlpha.700" isTruncated>{eventSummary(evt)}</Text>
|
||||||
|
<Eye size={12} style={{ flexShrink: 0, opacity: 0.3, marginLeft: 'auto' }} />
|
||||||
</Flex>
|
</Flex>
|
||||||
{evt.metrics && (evt.metrics.tokens_in != null || evt.metrics.latency_ms != null) && (
|
|
||||||
<HStack spacing={4} mt={1} ml="70px" color="whiteAlpha.400" fontSize="10px">
|
|
||||||
{evt.metrics.tokens_in != null && <Text>tokens: {evt.metrics.tokens_in}/{evt.metrics.tokens_out}</Text>}
|
|
||||||
{evt.metrics.latency_ms != null && evt.metrics.latency_ms > 0 && <Text>time: {evt.metrics.latency_ms}ms</Text>}
|
|
||||||
{evt.metrics.model && evt.metrics.model !== 'unknown' && <Text>model: {evt.metrics.model}</Text>}
|
|
||||||
</HStack>
|
|
||||||
)}
|
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
))}
|
||||||
{events.length === 0 && (
|
{events.length === 0 && (
|
||||||
|
|
@ -162,16 +280,21 @@ export const Dashboard: React.FC = () => {
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
{/* Node Inspector Drawer */}
|
{/* Unified Inspector Drawer (single event or all node events) */}
|
||||||
<Drawer isOpen={isOpen} placement="right" onClose={onClose} size="md">
|
<Drawer isOpen={isOpen} placement="right" onClose={onClose} size="md">
|
||||||
<DrawerOverlay backdropFilter="blur(4px)" />
|
<DrawerOverlay backdropFilter="blur(4px)" />
|
||||||
<DrawerContent bg="slate.900" color="white" borderLeft="1px solid" borderColor="whiteAlpha.200">
|
<DrawerContent bg="slate.900" color="white" borderLeft="1px solid" borderColor="whiteAlpha.200">
|
||||||
|
<DrawerCloseButton />
|
||||||
<DrawerHeader borderBottomWidth="1px" borderColor="whiteAlpha.100">
|
<DrawerHeader borderBottomWidth="1px" borderColor="whiteAlpha.100">
|
||||||
Node Inspector: {selectedNode?.agent}
|
{drawerTitle}
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
<DrawerBody>
|
<DrawerBody py={4}>
|
||||||
{/* Inspector content would go here */}
|
{drawerMode === 'event' && selectedEvent && (
|
||||||
<Text>Detailed metrics and raw JSON responses for the selected node.</Text>
|
<EventDetail event={selectedEvent} />
|
||||||
|
)}
|
||||||
|
{drawerMode === 'node' && selectedNodeId && (
|
||||||
|
<NodeEventsDetail nodeId={selectedNodeId} events={events} />
|
||||||
|
)}
|
||||||
</DrawerBody>
|
</DrawerBody>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ const AgentNode = ({ data }: NodeProps) => {
|
||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
minW="180px"
|
minW="180px"
|
||||||
boxShadow="0 0 15px rgba(0,0,0,0.5)"
|
boxShadow="0 0 15px rgba(0,0,0,0.5)"
|
||||||
|
cursor="pointer"
|
||||||
|
_hover={{ borderColor: 'cyan.300', boxShadow: '0 0 20px rgba(79,209,197,0.3)' }}
|
||||||
>
|
>
|
||||||
<Handle type="target" position={Position.Top} />
|
<Handle type="target" position={Position.Top} />
|
||||||
|
|
||||||
|
|
@ -51,6 +53,9 @@ const AgentNode = ({ data }: NodeProps) => {
|
||||||
<Flex align="center" gap={2}>
|
<Flex align="center" gap={2}>
|
||||||
<Icon as={getIcon(data.agent)} color={getStatusColor(data.status)} boxSize={4} />
|
<Icon as={getIcon(data.agent)} color={getStatusColor(data.status)} boxSize={4} />
|
||||||
<Text fontSize="sm" fontWeight="bold" color="white">{data.agent}</Text>
|
<Text fontSize="sm" fontWeight="bold" color="white">{data.agent}</Text>
|
||||||
|
{data.status === 'completed' && (
|
||||||
|
<Badge colorScheme="green" fontSize="2xs" ml="auto">Done</Badge>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
||||||
<Box height="1px" bg="whiteAlpha.200" width="100%" />
|
<Box height="1px" bg="whiteAlpha.200" width="100%" />
|
||||||
|
|
@ -60,7 +65,7 @@ const AgentNode = ({ data }: NodeProps) => {
|
||||||
<Icon as={Clock} boxSize={3} color="whiteAlpha.500" />
|
<Icon as={Clock} boxSize={3} color="whiteAlpha.500" />
|
||||||
<Text fontSize="2xs" color="whiteAlpha.600">{data.metrics?.latency_ms || 0}ms</Text>
|
<Text fontSize="2xs" color="whiteAlpha.600">{data.metrics?.latency_ms || 0}ms</Text>
|
||||||
</Flex>
|
</Flex>
|
||||||
{data.metrics?.model && (
|
{data.metrics?.model && data.metrics.model !== 'unknown' && (
|
||||||
<Badge variant="outline" fontSize="2xs" colorScheme="blue">{data.metrics.model}</Badge>
|
<Badge variant="outline" fontSize="2xs" colorScheme="blue">{data.metrics.model}</Badge>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|
@ -95,9 +100,10 @@ const nodeTypes = {
|
||||||
|
|
||||||
interface AgentGraphProps {
|
interface AgentGraphProps {
|
||||||
events: AgentEvent[];
|
events: AgentEvent[];
|
||||||
|
onNodeClick?: (nodeId: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AgentGraph: React.FC<AgentGraphProps> = ({ events }) => {
|
export const AgentGraph: React.FC<AgentGraphProps> = ({ events, onNodeClick }) => {
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
const [nodes, setNodes, onNodesChange] = useNodesState([]);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
// Track which node_ids we have already added so we never duplicate
|
// Track which node_ids we have already added so we never duplicate
|
||||||
|
|
@ -193,6 +199,10 @@ export const AgentGraph: React.FC<AgentGraphProps> = ({ events }) => {
|
||||||
}
|
}
|
||||||
}, [events.length, setNodes, setEdges]);
|
}, [events.length, setNodes, setEdges]);
|
||||||
|
|
||||||
|
const handleNodeClick = useCallback((_event: React.MouseEvent, node: Node) => {
|
||||||
|
onNodeClick?.(node.id);
|
||||||
|
}, [onNodeClick]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box height="100%" width="100%" bg="slate.950">
|
<Box height="100%" width="100%" bg="slate.950">
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
|
|
@ -200,6 +210,7 @@ export const AgentGraph: React.FC<AgentGraphProps> = ({ events }) => {
|
||||||
edges={edges}
|
edges={edges}
|
||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
|
onNodeClick={handleNodeClick}
|
||||||
nodeTypes={nodeTypes}
|
nodeTypes={nodeTypes}
|
||||||
fitView
|
fitView
|
||||||
>
|
>
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "bundler",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true,
|
"composite": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "bundler",
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"include": ["vite.config.ts"]
|
"include": ["vite.config.ts"]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue