New frontend and tools
This commit is contained in:
@ -3,25 +3,62 @@
|
||||
@tailwind utilities;
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
--foreground-rgb: 255, 255, 255;
|
||||
--background-start-rgb: 0, 0, 0;
|
||||
--background-end-rgb: 0, 0, 0;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--foreground);
|
||||
background: var(--background);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
color: rgb(var(--foreground-rgb));
|
||||
background: linear-gradient(
|
||||
to bottom,
|
||||
transparent,
|
||||
rgb(var(--background-end-rgb))
|
||||
)
|
||||
rgb(var(--background-start-rgb));
|
||||
font-family: 'Noto Sans Mono', monospace;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
/* Custom styles from the original index.html */
|
||||
.thinking-section {
|
||||
margin-bottom: 20px;
|
||||
border-left: 2px solid #444;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.thought-summary {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
padding: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.thought-summary.plan { background-color: #2c3e50; }
|
||||
.thought-summary.decision { background-color: #34495e; }
|
||||
.thought-summary.tool_call { background-color: #16a085; }
|
||||
.thought-summary.tool_result { background-color: #27ae60; }
|
||||
.thought-summary.think_more { background-color: #2980b9; }
|
||||
.thought-summary.answer { background-color: #8e44ad; }
|
||||
|
||||
.thought-details {
|
||||
display: none;
|
||||
margin-left: 20px;
|
||||
border-left: 2px solid #444;
|
||||
padding-left: 10px;
|
||||
margin-bottom: 10px;
|
||||
white-space: pre-wrap;
|
||||
font-family: 'Noto Sans Mono', monospace;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.collapsible::before {
|
||||
content: '▶ ';
|
||||
display: inline-block;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.collapsible.open::before {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Add any other custom styles from the original index.html here */
|
||||
|
@ -1,35 +1,31 @@
|
||||
import type { Metadata } from "next";
|
||||
import localFont from "next/font/local";
|
||||
import "./globals.css";
|
||||
import './globals.css'
|
||||
import { Inter } from 'next/font/google'
|
||||
|
||||
const geistSans = localFont({
|
||||
src: "./fonts/GeistVF.woff",
|
||||
variable: "--font-geist-sans",
|
||||
weight: "100 900",
|
||||
});
|
||||
const geistMono = localFont({
|
||||
src: "./fonts/GeistMonoVF.woff",
|
||||
variable: "--font-geist-mono",
|
||||
weight: "100 900",
|
||||
});
|
||||
const inter = Inter({ subsets: ['latin'] })
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
export const metadata = {
|
||||
title: 'DWS Intelligence',
|
||||
description: 'AI-powered chat application',
|
||||
}
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
}: {
|
||||
children: React.ReactNode
|
||||
}) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
<head>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.4/moment.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.1/dist/chartjs-adapter-moment.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/default.min.css" />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+Mono:wght@400;700&display=swap" rel="stylesheet" />
|
||||
</head>
|
||||
<body className={inter.className}>{children}</body>
|
||||
</html>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
@ -1,101 +1,44 @@
|
||||
import Image from "next/image";
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import ChatArea from '../components/ChatArea'
|
||||
import Sidebar from '../components/Sidebar'
|
||||
import useSocket from '../hooks/useSocket'
|
||||
import useLocalStorage from '../hooks/useLocalStorage'
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="https://nextjs.org/icons/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
|
||||
app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li>Save and see your changes instantly.</li>
|
||||
</ol>
|
||||
const [currentChatId, setCurrentChatId] = useState<string | null>(null)
|
||||
const [chats, setChats] = useLocalStorage('chats', {})
|
||||
const socket = useSocket()
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="https://nextjs.org/icons/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="https://nextjs.org/icons/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="https://nextjs.org/icons/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="https://nextjs.org/icons/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
useEffect(() => {
|
||||
if (Object.keys(chats).length === 0) {
|
||||
createNewChat()
|
||||
} else {
|
||||
setCurrentChatId(Object.keys(chats)[0])
|
||||
}
|
||||
}, [])
|
||||
|
||||
const createNewChat = () => {
|
||||
const chatId = Date.now().toString()
|
||||
setChats(prevChats => ({
|
||||
...prevChats,
|
||||
[chatId]: { messages: [], thinkingSections: [] }
|
||||
}))
|
||||
setCurrentChatId(chatId)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<ChatArea
|
||||
currentChatId={currentChatId}
|
||||
setCurrentChatId={setCurrentChatId}
|
||||
chats={chats}
|
||||
setChats={setChats}
|
||||
createNewChat={createNewChat}
|
||||
socket={socket}
|
||||
/>
|
||||
<Sidebar socket={socket} />
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
133
dewey/components/ChatArea.tsx
Normal file
133
dewey/components/ChatArea.tsx
Normal file
@ -0,0 +1,133 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import ChatTabs from './ChatTabs'
|
||||
import ChatContainer from './ChatContainer'
|
||||
import UserInput from './UserInput'
|
||||
|
||||
export default function ChatArea({ currentChatId, setCurrentChatId, chats, setChats, createNewChat, socket }) {
|
||||
const [userInput, setUserInput] = useState('')
|
||||
|
||||
const sendMessage = () => {
|
||||
if (userInput.trim() && currentChatId) {
|
||||
const newMessage = { content: userInput, isUser: true }
|
||||
setChats(prevChats => ({
|
||||
...prevChats,
|
||||
[currentChatId]: {
|
||||
...prevChats[currentChatId],
|
||||
messages: [...prevChats[currentChatId].messages, newMessage],
|
||||
thinkingSections: [...prevChats[currentChatId].thinkingSections, { thoughts: [] }]
|
||||
}
|
||||
}))
|
||||
socket.emit('chat_request', {
|
||||
message: userInput,
|
||||
conversation_history: chats[currentChatId].messages
|
||||
.filter(m => !m.isUser).map(m => ({ role: 'assistant', content: m.content }))
|
||||
.concat(chats[currentChatId].messages.filter(m => m.isUser).map(m => ({ role: 'user', content: m.content })))
|
||||
})
|
||||
setUserInput('')
|
||||
}
|
||||
}
|
||||
|
||||
const switchToChat = (chatId: string) => {
|
||||
setCurrentChatId(chatId);
|
||||
}
|
||||
|
||||
const closeChat = (chatId: string) => {
|
||||
if (window.confirm('Are you sure you want to close this chat?')) {
|
||||
setChats(prevChats => {
|
||||
const newChats = { ...prevChats };
|
||||
delete newChats[chatId];
|
||||
return newChats;
|
||||
});
|
||||
if (currentChatId === chatId) {
|
||||
const remainingChatIds = Object.keys(chats).filter(id => id !== chatId);
|
||||
if (remainingChatIds.length > 0) {
|
||||
switchToChat(remainingChatIds[0]);
|
||||
} else {
|
||||
createNewChat();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on('thinking', (data) => {
|
||||
// Handle thinking event
|
||||
setChats(prevChats => ({
|
||||
...prevChats,
|
||||
[currentChatId]: {
|
||||
...prevChats[currentChatId],
|
||||
thinkingSections: [
|
||||
...prevChats[currentChatId].thinkingSections,
|
||||
{ thoughts: [{ type: 'thinking', content: data.step }] }
|
||||
]
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on('thought', (data) => {
|
||||
// Handle thought event
|
||||
setChats(prevChats => ({
|
||||
...prevChats,
|
||||
[currentChatId]: {
|
||||
...prevChats[currentChatId],
|
||||
thinkingSections: prevChats[currentChatId].thinkingSections.map((section, index) =>
|
||||
index === prevChats[currentChatId].thinkingSections.length - 1
|
||||
? { ...section, thoughts: [...section.thoughts, data] }
|
||||
: section
|
||||
)
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on('chat_response', (data) => {
|
||||
// Handle chat response event
|
||||
setChats(prevChats => ({
|
||||
...prevChats,
|
||||
[currentChatId]: {
|
||||
...prevChats[currentChatId],
|
||||
messages: [...prevChats[currentChatId].messages, { content: data.response, isUser: false }]
|
||||
}
|
||||
}));
|
||||
});
|
||||
|
||||
socket.on('error', (data) => {
|
||||
// Handle error event
|
||||
console.error('Error:', data.message);
|
||||
// You might want to display this error to the user
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (socket) {
|
||||
socket.off('thinking');
|
||||
socket.off('thought');
|
||||
socket.off('chat_response');
|
||||
socket.off('error');
|
||||
}
|
||||
};
|
||||
}, [socket, currentChatId, setChats]);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col flex-1">
|
||||
<ChatTabs
|
||||
chats={chats}
|
||||
currentChatId={currentChatId}
|
||||
createNewChat={createNewChat}
|
||||
switchToChat={switchToChat}
|
||||
closeChat={closeChat}
|
||||
/>
|
||||
{currentChatId && (
|
||||
<ChatContainer
|
||||
currentChat={chats[currentChatId]}
|
||||
socket={socket}
|
||||
/>
|
||||
)}
|
||||
<UserInput
|
||||
value={userInput}
|
||||
onChange={setUserInput}
|
||||
onSend={sendMessage}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
85
dewey/components/ChatContainer.tsx
Normal file
85
dewey/components/ChatContainer.tsx
Normal file
@ -0,0 +1,85 @@
|
||||
import React, { useEffect, useRef } from 'react';
|
||||
import { marked } from 'marked';
|
||||
|
||||
interface ChatContainerProps {
|
||||
currentChat: {
|
||||
messages: Array<{ content: string; isUser: boolean }>;
|
||||
thinkingSections: Array<{ thoughts: Array<{ type: string; content: string; details?: string }> }>;
|
||||
} | null;
|
||||
socket: any;
|
||||
}
|
||||
|
||||
const ChatContainer: React.FC<ChatContainerProps> = ({ currentChat, socket }) => {
|
||||
const chatContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (chatContainerRef.current) {
|
||||
chatContainerRef.current.scrollTop = chatContainerRef.current.scrollHeight;
|
||||
}
|
||||
}, [currentChat]);
|
||||
|
||||
if (!currentChat) return null;
|
||||
|
||||
return (
|
||||
<div ref={chatContainerRef} className="flex-1 overflow-y-auto p-4 bg-gray-900">
|
||||
{currentChat.messages.map((message, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={`mb-4 ${
|
||||
message.isUser ? 'text-right text-cyan-300' : 'text-left text-white'
|
||||
}`}
|
||||
>
|
||||
<div
|
||||
className={`inline-block p-2 rounded-lg ${
|
||||
message.isUser ? 'bg-cyan-800' : 'bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
{message.isUser ? (
|
||||
message.content
|
||||
) : (
|
||||
<div dangerouslySetInnerHTML={{ __html: marked(message.content) }} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{currentChat.thinkingSections.map((section, sectionIndex) => (
|
||||
<div key={sectionIndex} className="mb-4 border-l-2 border-gray-600 pl-4">
|
||||
{section.thoughts.map((thought, thoughtIndex) => (
|
||||
<div key={thoughtIndex} className="mb-2">
|
||||
<div className={`font-bold ${getThoughtColor(thought.type)}`}>
|
||||
{thought.type}:
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{ __html: marked(thought.content) }} />
|
||||
{thought.details && (
|
||||
<pre className="mt-2 p-2 bg-gray-800 rounded">
|
||||
{thought.details}
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
function getThoughtColor(type: string): string {
|
||||
switch (type.toLowerCase()) {
|
||||
case 'plan':
|
||||
return 'text-blue-400';
|
||||
case 'decision':
|
||||
return 'text-green-400';
|
||||
case 'tool_call':
|
||||
return 'text-yellow-400';
|
||||
case 'tool_result':
|
||||
return 'text-purple-400';
|
||||
case 'think_more':
|
||||
return 'text-pink-400';
|
||||
case 'answer':
|
||||
return 'text-red-400';
|
||||
default:
|
||||
return 'text-gray-400';
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatContainer;
|
48
dewey/components/ChatTabs.tsx
Normal file
48
dewey/components/ChatTabs.tsx
Normal file
@ -0,0 +1,48 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ChatTabsProps {
|
||||
chats: Record<string, any>;
|
||||
currentChatId: string | null;
|
||||
createNewChat: () => void;
|
||||
switchToChat: (chatId: string) => void;
|
||||
closeChat: (chatId: string) => void;
|
||||
}
|
||||
|
||||
const ChatTabs: React.FC<ChatTabsProps> = ({ chats, currentChatId, createNewChat, switchToChat, closeChat }) => {
|
||||
return (
|
||||
<div className="flex bg-gray-800 p-2">
|
||||
{Object.keys(chats).map((chatId) => (
|
||||
<div
|
||||
key={chatId}
|
||||
className={`px-4 py-2 mr-2 rounded-t-lg flex items-center ${
|
||||
chatId === currentChatId ? 'bg-gray-600' : 'bg-gray-700'
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
onClick={() => switchToChat(chatId)}
|
||||
className="flex-grow text-left"
|
||||
>
|
||||
Chat {chatId}
|
||||
</button>
|
||||
<button
|
||||
className="ml-2 text-red-500 hover:text-red-700"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
closeChat(chatId);
|
||||
}}
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
<button
|
||||
className="px-4 py-2 bg-green-600 rounded-t-lg"
|
||||
onClick={createNewChat}
|
||||
>
|
||||
+ New Chat
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatTabs;
|
129
dewey/components/Sidebar.tsx
Normal file
129
dewey/components/Sidebar.tsx
Normal file
@ -0,0 +1,129 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const Chart = dynamic(() => import('chart.js/auto').then((mod) => mod.Chart), {
|
||||
ssr: false,
|
||||
});
|
||||
|
||||
interface SidebarProps {
|
||||
socket: any;
|
||||
}
|
||||
|
||||
const Sidebar: React.FC<SidebarProps> = ({ socket }) => {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
const chartRefs = useRef<{ [key: string]: any }>({
|
||||
cpu: null,
|
||||
memory: null,
|
||||
disk: null,
|
||||
gpu: null,
|
||||
gpuMemory: null,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (socket) {
|
||||
socket.on('system_resources', (data: any) => {
|
||||
updateCharts(data);
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (socket) {
|
||||
socket.off('system_resources');
|
||||
}
|
||||
};
|
||||
}, [socket]);
|
||||
|
||||
useEffect(() => {
|
||||
const initCharts = async () => {
|
||||
const ChartJS = await Chart;
|
||||
initializeCharts(ChartJS);
|
||||
};
|
||||
initCharts();
|
||||
|
||||
return () => {
|
||||
Object.values(chartRefs.current).forEach(chart => chart?.destroy());
|
||||
};
|
||||
}, []);
|
||||
|
||||
const initializeCharts = (ChartJS: any) => {
|
||||
const chartConfig = {
|
||||
type: 'line',
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'second',
|
||||
},
|
||||
},
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
},
|
||||
},
|
||||
animation: false,
|
||||
},
|
||||
data: {
|
||||
datasets: [{
|
||||
data: [],
|
||||
borderColor: 'rgb(75, 192, 192)',
|
||||
tension: 0.1,
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
['cpu', 'memory', 'disk', 'gpu', 'gpuMemory'].forEach(chartName => {
|
||||
const ctx = document.getElementById(`${chartName}Chart`) as HTMLCanvasElement;
|
||||
if (ctx) {
|
||||
chartRefs.current[chartName] = new ChartJS(ctx, chartConfig);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateCharts = (data: any) => {
|
||||
const now = new Date();
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
const chartName = key.replace('_', '').toLowerCase();
|
||||
const chart = chartRefs.current[chartName];
|
||||
if (chart) {
|
||||
chart.data.datasets[0].data.push({x: now, y: value});
|
||||
chart.update('none');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`w-80 bg-gray-800 p-4 ${isCollapsed ? 'hidden' : ''}`}>
|
||||
<button
|
||||
onClick={() => setIsCollapsed(!isCollapsed)}
|
||||
className="mb-4 px-4 py-2 bg-gray-700 text-white rounded-lg"
|
||||
>
|
||||
{isCollapsed ? 'Show Charts' : 'Hide Charts'}
|
||||
</button>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">CPU Load</h3>
|
||||
<canvas id="cpuChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">Memory Usage</h3>
|
||||
<canvas id="memoryChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">Disk I/O</h3>
|
||||
<canvas id="diskChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">GPU Load</h3>
|
||||
<canvas id="gpuChart"></canvas>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<h3 className="text-white mb-2">GPU Memory</h3>
|
||||
<canvas id="gpuMemoryChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Sidebar;
|
37
dewey/components/UserInput.tsx
Normal file
37
dewey/components/UserInput.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
|
||||
interface UserInputProps {
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
onSend: () => void;
|
||||
}
|
||||
|
||||
const UserInput: React.FC<UserInputProps> = ({ value, onChange, onSend }) => {
|
||||
const handleKeyPress = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
onSend();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 bg-gray-800">
|
||||
<textarea
|
||||
value={value}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
className="w-full p-2 bg-gray-700 text-white rounded-lg resize-none"
|
||||
rows={3}
|
||||
placeholder="Type your message here..."
|
||||
/>
|
||||
<button
|
||||
onClick={onSend}
|
||||
className="mt-2 px-4 py-2 bg-blue-600 text-white rounded-lg"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInput;
|
23
dewey/hooks/useLocalStorage.ts
Normal file
23
dewey/hooks/useLocalStorage.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
export default function useLocalStorage(key, initialValue) {
|
||||
const [storedValue, setStoredValue] = useState(() => {
|
||||
try {
|
||||
const item = window.localStorage.getItem(key)
|
||||
return item ? JSON.parse(item) : initialValue
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
return initialValue
|
||||
}
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
window.localStorage.setItem(key, JSON.stringify(storedValue))
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}, [key, storedValue])
|
||||
|
||||
return [storedValue, setStoredValue]
|
||||
}
|
14
dewey/hooks/useSocket.ts
Normal file
14
dewey/hooks/useSocket.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import io from 'socket.io-client'
|
||||
|
||||
export default function useSocket() {
|
||||
const [socket, setSocket] = useState<any>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const newSocket = io('http://localhost:5001')
|
||||
setSocket(newSocket)
|
||||
return () => newSocket.close()
|
||||
}, [])
|
||||
|
||||
return socket
|
||||
}
|
292
dewey/package-lock.json
generated
292
dewey/package-lock.json
generated
@ -8,19 +8,24 @@
|
||||
"name": "dewey",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"next": "14.2.13",
|
||||
"react": "^18",
|
||||
"react-dom": "^18"
|
||||
"autoprefixer": "^10.4.20",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"marked": "^4.3.0",
|
||||
"next": "^14.2.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"socket.io-client": "^4.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/node": "^20.16.10",
|
||||
"@types/react": "^18.3.10",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.13",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5"
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
@ -467,6 +472,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@socket.io/component-emitter": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
|
||||
"integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@swc/counter": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||
@ -1027,6 +1038,43 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.20",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
|
||||
"integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.23.3",
|
||||
"caniuse-lite": "^1.0.30001646",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.0.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
@ -1107,6 +1155,38 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.24.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz",
|
||||
"integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001663",
|
||||
"electron-to-chromium": "^1.5.28",
|
||||
"node-releases": "^2.0.18",
|
||||
"update-browserslist-db": "^1.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"browserslist": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/busboy": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||
@ -1195,6 +1275,22 @@
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/chart.js": {
|
||||
"version": "3.9.1",
|
||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz",
|
||||
"integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/chartjs-adapter-moment": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/chartjs-adapter-moment/-/chartjs-adapter-moment-1.0.1.tgz",
|
||||
"integrity": "sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"chart.js": ">=3.0.0",
|
||||
"moment": "^2.10.2"
|
||||
}
|
||||
},
|
||||
"node_modules/chokidar": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
|
||||
@ -1376,7 +1472,6 @@
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
"integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
@ -1500,6 +1595,12 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.5.29",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz",
|
||||
"integrity": "sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
@ -1507,6 +1608,28 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/engine.io-client": {
|
||||
"version": "6.6.1",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.1.tgz",
|
||||
"integrity": "sha512-aYuoak7I+R83M/BBPIOs2to51BmFIpC1wZe6zZzMrT2llVsHy5cvcmdsJgP2Qz6smHu+sD9oexiSUAVd8OfBPw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1",
|
||||
"engine.io-parser": "~5.2.1",
|
||||
"ws": "~8.17.1",
|
||||
"xmlhttprequest-ssl": "~2.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/engine.io-parser": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
|
||||
"integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/enhanced-resolve": {
|
||||
"version": "5.17.1",
|
||||
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz",
|
||||
@ -1708,6 +1831,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/escalade": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
@ -2322,6 +2454,19 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "patreon",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
@ -3368,6 +3513,18 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/marked": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz",
|
||||
"integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"marked": "bin/marked.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
@ -3425,11 +3582,20 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/moment": {
|
||||
"version": "2.30.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
|
||||
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mz": {
|
||||
@ -3547,6 +3713,12 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.18",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
|
||||
"integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@ -3557,6 +3729,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
@ -3883,7 +4064,6 @@
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@ -4039,7 +4219,6 @@
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/prelude-ls": {
|
||||
@ -4451,6 +4630,34 @@
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-client": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.0.tgz",
|
||||
"integrity": "sha512-C0jdhD5yQahMws9alf/yvtsMGTaIDBnZ8Rb5HU56svyq0l5LIrGzIDZZD5pHQlmzxLuU91Gz+VpQMKgCTNYtkw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.2",
|
||||
"engine.io-client": "~6.6.1",
|
||||
"socket.io-parser": "~4.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/socket.io-parser": {
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
|
||||
"integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@socket.io/component-emitter": "~3.1.0",
|
||||
"debug": "~4.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
@ -5044,6 +5251,36 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
|
||||
"integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/browserslist"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"escalade": "^3.2.0",
|
||||
"picocolors": "^1.1.0"
|
||||
},
|
||||
"bin": {
|
||||
"update-browserslist-db": "cli.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"browserslist": ">= 4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uri-js": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
@ -5278,6 +5515,35 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.17.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
|
||||
"integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlhttprequest-ssl": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.1.tgz",
|
||||
"integrity": "sha512-ptjR8YSJIXoA3Mbv5po7RtSYHO6mZr8s7i5VGmEk7QY2pQWyT1o0N+W1gKbOyJPUCGXGnuw0wqe8f0L6Y0ny7g==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/yaml": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
|
||||
|
@ -9,18 +9,23 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18",
|
||||
"react-dom": "^18",
|
||||
"next": "14.2.13"
|
||||
"autoprefixer": "^10.4.20",
|
||||
"chart.js": "^3.9.1",
|
||||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"marked": "^4.3.0",
|
||||
"next": "^14.2.13",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"socket.io-client": "^4.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"postcss": "^8",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"@types/node": "^20.16.10",
|
||||
"@types/react": "^18.3.10",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"eslint": "^8",
|
||||
"eslint-config-next": "14.2.13"
|
||||
"eslint-config-next": "14.2.13",
|
||||
"postcss": "^8.4.47",
|
||||
"tailwindcss": "^3.4.13",
|
||||
"typescript": "^5.6.2"
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user