diff --git a/.dockerignore b/.dockerignore index 994e0a0..328c5e5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,19 +1,50 @@ +# Git .git .gitignore -.venv -venv -.env -env/ + +# Python __pycache__/ -*.pyc +*.py[cod] *.pyo *.pyd .Python +*.so +*.egg-info/ +dist/ +build/ + +# Virtual envs +.venv +venv +env/ + +# Env files +.env +.env.* +!.env.example + +# Editors +.vscode/ +.idea/ + +# Tests +tests/ test/ -README.md -docker-compose.yml + +# Node +node_modules/ +frontend/node_modules/ +.next/ +frontend/.next/ +npm-debug.log* +yarn-error.log* + +# Docker Dockerfile +docker-compose.yml .dockerignore + + +.files/ .chainlit/translations/* -!.chainlit/translations/en-US.json -.files/ \ No newline at end of file +!.chainlit/translations/en-US.json \ No newline at end of file diff --git a/backend/__init__.py b/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config.yaml b/backend/config.yaml similarity index 100% rename from config.yaml rename to backend/config.yaml diff --git a/backend/mcp_server/Dockerfile b/backend/mcp_server/Dockerfile index ce367be..baf06b2 100644 --- a/backend/mcp_server/Dockerfile +++ b/backend/mcp_server/Dockerfile @@ -12,4 +12,4 @@ COPY backend/ ./backend/ EXPOSE 8001 -CMD ["python", "-m", "mcp_server.server"] \ No newline at end of file +CMD ["python", "-m", "backend.mcp_server.mcp_server"] \ No newline at end of file diff --git a/compose.yaml b/compose.yaml index a5ff2b1..2f5e22e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,9 +1,22 @@ name: "legal-ai-assistant" services: + frontend: + build: + context: ./frontend + dockerfile: Dockerfile + restart: unless-stopped + ports: + - "3000:3000" + environment: + - NODE_ENV=production + depends_on: + backend: + condition: service_started + backend: build: context: . - dockerfile: backend/Dockerfile + dockerfile: /backend/Dockerfile restart: unless-stopped ports: - "8000:8000" @@ -26,18 +39,18 @@ services: ports: - "4000:4000" env_file: - - .env + - backend/.env environment: - GROQ_API_KEY=${GROQ_API_KEY} - GEMINI_API_KEY=${GEMINI_API_KEY} volumes: - - ./config.yaml:/app/config.yaml:ro + - ./backend/config.yaml:/app/config.yaml:ro command: ['--config', '/app/config.yaml', '--port', '4000'] mcp: build: context: . - dockerfile: backend/mcp_server/Dockerfile + dockerfile: /backend/mcp_server/Dockerfile restart: unless-stopped ports: - "8001:8001" diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..73e9741 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,14 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY package*.json ./ +RUN npm ci + +COPY . . + +RUN npm run build + +EXPOSE 3000 + +CMD ["npm", "start"] \ No newline at end of file diff --git a/frontend/app/api/chat/route.ts b/frontend/app/api/chat/route.ts index f4ec2f9..578c895 100644 --- a/frontend/app/api/chat/route.ts +++ b/frontend/app/api/chat/route.ts @@ -1,31 +1,19 @@ -import { openai } from "@ai-sdk/openai"; -import { frontendTools } from "@assistant-ui/react-ai-sdk"; -import { - JSONSchema7, - streamText, - convertToModelMessages, - type UIMessage, -} from "ai"; - export async function POST(req: Request) { - const { - messages, - system, - tools, - }: { - messages: UIMessage[]; - system?: string; - tools?: Record; - } = await req.json(); + const body = await req.json(); - const result = streamText({ - model: openai("gpt-5-nano"), - messages: await convertToModelMessages(messages), - system, - tools: { - ...frontendTools(tools ?? {}), + const response = await fetch("http://backend:8000/api/chat", { + method: "POST", + headers: { + "Content-Type": "application/json", }, + body: JSON.stringify(body), }); - return result.toUIMessageStreamResponse(); -} + return new Response(response.body, { + headers: { + "Content-Type": "text/event-stream", + "Cache-Control": "no-cache", + "X-Accel-Buffering": "no", + }, + }); +} \ No newline at end of file diff --git a/frontend/app/favicon.ico b/frontend/app/favicon.ico index 718d6fe..0882aaa 100644 Binary files a/frontend/app/favicon.ico and b/frontend/app/favicon.ico differ diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 73b2e3e..04c459a 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -47,50 +47,45 @@ :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.141 0.005 285.823); - --card: oklch(1 0 0); - --card-foreground: oklch(0.141 0.005 285.823); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.141 0.005 285.823); - --primary: oklch(0.21 0.006 285.885); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.967 0.001 286.375); - --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.967 0.001 286.375); - --muted-foreground: oklch(0.552 0.016 285.938); - --accent: oklch(0.967 0.001 286.375); - --accent-foreground: oklch(0.21 0.006 285.885); + --background: oklch(0.12 0.04 240); + --foreground: oklch(0.95 0.01 240); + --card: oklch(0.17 0.05 240); + --card-foreground: oklch(0.95 0.01 240); + --popover: oklch(0.17 0.05 240); + --popover-foreground: oklch(0.95 0.01 240); + --primary: oklch(0.6 0.18 240); + --primary-foreground: oklch(0.98 0 0); + --secondary: oklch(0.2 0.05 240); + --secondary-foreground: oklch(0.95 0.01 240); + --muted: oklch(0.2 0.05 240); + --muted-foreground: oklch(0.65 0.05 240); + --accent: oklch(0.22 0.06 240); + --accent-foreground: oklch(0.95 0.01 240); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.92 0.004 286.32); - --input: oklch(0.92 0.004 286.32); - --ring: oklch(0.705 0.015 286.067); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); + --border: oklch(1 0 0 / 8%); + --input: oklch(1 0 0 / 12%); + --ring: oklch(0.5 0.15 240); } .dark { - --background: oklch(0.141 0.005 285.823); - --foreground: oklch(0.985 0 0); - --card: oklch(0.21 0.006 285.885); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.21 0.006 285.885); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.92 0.004 286.32); - --primary-foreground: oklch(0.21 0.006 285.885); - --secondary: oklch(0.274 0.006 286.033); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.274 0.006 286.033); - --muted-foreground: oklch(0.705 0.015 286.067); - --accent: oklch(0.274 0.006 286.033); - --accent-foreground: oklch(0.985 0 0); + --background: oklch(0.12 0.04 240); + --foreground: oklch(0.95 0.01 240); + --card: oklch(0.17 0.05 240); + --card-foreground: oklch(0.95 0.01 240); + --popover: oklch(0.17 0.05 240); + --popover-foreground: oklch(0.95 0.01 240); + --primary: oklch(0.6 0.18 240); + --primary-foreground: oklch(0.98 0 0); + --secondary: oklch(0.2 0.05 240); + --secondary-foreground: oklch(0.95 0.01 240); + --muted: oklch(0.2 0.05 240); + --muted-foreground: oklch(0.65 0.05 240); + --accent: oklch(0.22 0.06 240); + --accent-foreground: oklch(0.95 0.01 240); --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.552 0.016 285.938); + --border: oklch(1 0 0 / 8%); + --input: oklch(1 0 0 / 12%); + --ring: oklch(0.5 0.15 240); --chart-1: oklch(0.488 0.243 264.376); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); diff --git a/frontend/components/assistant-ui/attachment.tsx b/frontend/components/assistant-ui/attachment.tsx deleted file mode 100644 index be735b0..0000000 --- a/frontend/components/assistant-ui/attachment.tsx +++ /dev/null @@ -1,222 +0,0 @@ -"use client"; - -import { PropsWithChildren, useEffect, useState, type FC } from "react"; -import { XIcon, PlusIcon, FileText } from "lucide-react"; -import { - AttachmentPrimitive, - ComposerPrimitive, - MessagePrimitive, - useAuiState, - useAui, -} from "@assistant-ui/react"; -import { useShallow } from "zustand/shallow"; -import { - Tooltip, - TooltipContent, - TooltipTrigger, -} from "@/components/ui/tooltip"; -import { - Dialog, - DialogTitle, - DialogContent, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; -import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; -import { cn } from "@/lib/utils"; - -const useFileSrc = (file: File | undefined) => { - const [src, setSrc] = useState(undefined); - - useEffect(() => { - if (!file) { - setSrc(undefined); - return; - } - - const objectUrl = URL.createObjectURL(file); - setSrc(objectUrl); - - return () => { - URL.revokeObjectURL(objectUrl); - }; - }, [file]); - - return src; -}; - -const useAttachmentSrc = () => { - const { file, src } = useAuiState( - useShallow((s): { file?: File; src?: string } => { - if (s.attachment.type !== "image") return {}; - if (s.attachment.file) return { file: s.attachment.file }; - const src = s.attachment.content?.filter((c) => c.type === "image")[0] - ?.image; - if (!src) return {}; - return { src }; - }), - ); - - return useFileSrc(file) ?? src; -}; - -type AttachmentPreviewProps = { - src: string; -}; - -const AttachmentPreview: FC = ({ src }) => { - const [isLoaded, setIsLoaded] = useState(false); - return ( - Image Preview setIsLoaded(true)} - /> - ); -}; - -const AttachmentPreviewDialog: FC = ({ children }) => { - const src = useAttachmentSrc(); - - if (!src) return children; - - return ( - - - {children} - - - - Image Attachment Preview - -
- -
-
-
- ); -}; - -const AttachmentThumb: FC = () => { - const src = useAttachmentSrc(); - - return ( - - - - - - - ); -}; - -const AttachmentUI: FC = () => { - const aui = useAui(); - const isComposer = aui.attachment.source !== "message"; - - const isImage = useAuiState((s) => s.attachment.type === "image"); - const typeLabel = useAuiState((s) => { - const type = s.attachment.type; - switch (type) { - case "image": - return "Image"; - case "document": - return "Document"; - case "file": - return "File"; - default: - return type; - } - }); - - return ( - - - - -
- -
-
-
- {isComposer && } -
- - - -
- ); -}; - -const AttachmentRemove: FC = () => { - return ( - - - - - - ); -}; - -export const UserMessageAttachments: FC = () => { - return ( -
- - {() => } - -
- ); -}; - -export const ComposerAttachments: FC = () => { - return ( -
- - {() => } - -
- ); -}; - -export const ComposerAddAttachment: FC = () => { - return ( - - - - - - ); -}; diff --git a/frontend/components/assistant-ui/composer.tsx b/frontend/components/assistant-ui/composer.tsx new file mode 100644 index 0000000..8864f75 --- /dev/null +++ b/frontend/components/assistant-ui/composer.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { type FC } from "react"; +import { ComposerPrimitive, AuiIf } from "@assistant-ui/react"; +import { ArrowUpIcon, SquareIcon } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { TooltipIconButton } from "./tooltip-icon-button"; + +const ComposerAction: FC = () => { + return ( +
+ !s.thread.isRunning}> + + + + + + + s.thread.isRunning}> + + + + +
+ ); +}; + +export const ThreadComposer: FC = () => { + return ( + +
+ + +
+
+ ); +}; \ No newline at end of file diff --git a/frontend/components/assistant-ui/header.tsx b/frontend/components/assistant-ui/header.tsx new file mode 100644 index 0000000..c20d319 --- /dev/null +++ b/frontend/components/assistant-ui/header.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { type FC } from "react"; +import { SquarePenIcon, ChevronDownIcon, ScaleIcon } from "lucide-react"; + +export const ThreadHeader: FC = () => { + return ( +
+ + +
+ + Legal AI Assistant + + +
+ +
+ +
+
+ ); +}; \ No newline at end of file diff --git a/frontend/components/assistant-ui/messages.tsx b/frontend/components/assistant-ui/messages.tsx new file mode 100644 index 0000000..4e3acc6 --- /dev/null +++ b/frontend/components/assistant-ui/messages.tsx @@ -0,0 +1,160 @@ +"use client"; + +import { type FC } from "react"; +import { + ActionBarMorePrimitive, + ActionBarPrimitive, + AuiIf, + BranchPickerPrimitive, + ErrorPrimitive, + MessagePrimitive, + useAuiState, +} from "@assistant-ui/react"; +import { + CheckIcon, + ChevronLeftIcon, + ChevronRightIcon, + CopyIcon, + DownloadIcon, + MoreHorizontalIcon, + RefreshCwIcon, + ScaleIcon, +} from "lucide-react"; +import { cn } from "@/lib/utils"; +import { MarkdownText } from "./markdown-text"; +import { ToolFallback } from "./tool-fallback"; +import { TooltipIconButton } from "./tooltip-icon-button"; + +const MessageError: FC = () => { + return ( + + + + + + ); +}; + +const AssistantActionBar: FC = () => { + return ( + + + + s.message.isCopied}> + + + !s.message.isCopied}> + + + + + + + + + + + + + + + + + + + + Export ako Markdown + + + + + + ); +}; + +export const BranchPicker: FC = ({ + className, + ...rest +}) => { + return ( + + + + + + + + / + + + + + + + + ); +}; + +export const AssistantMessage: FC = () => { + return ( + +
+
+ +
+
+
+ + {({ part }) => { + if (part.type === "text") return ; + if (part.type === "tool-call") + return part.toolUI ?? ; + return null; + }} + + +
+
+ + +
+
+
+
+ ); +}; + +export const UserMessage: FC = () => { + return ( + +
+
+ +
+
+ +
+ ); +}; \ No newline at end of file diff --git a/frontend/components/assistant-ui/thread.tsx b/frontend/components/assistant-ui/thread.tsx index 102adc8..b218eee 100644 --- a/frontend/components/assistant-ui/thread.tsx +++ b/frontend/components/assistant-ui/thread.tsx @@ -1,75 +1,17 @@ -import { - ComposerAddAttachment, - ComposerAttachments, - UserMessageAttachments, -} from "@/components/assistant-ui/attachment"; -import { MarkdownText } from "@/components/assistant-ui/markdown-text"; -import { ToolFallback } from "@/components/assistant-ui/tool-fallback"; -import { TooltipIconButton } from "@/components/assistant-ui/tooltip-icon-button"; -import { Button } from "@/components/ui/button"; -import { cn } from "@/lib/utils"; -import { - ActionBarMorePrimitive, - ActionBarPrimitive, - AuiIf, - BranchPickerPrimitive, - ComposerPrimitive, - ErrorPrimitive, - MessagePrimitive, - SuggestionPrimitive, - ThreadPrimitive, - useAuiState, -} from "@assistant-ui/react"; -import { - ArrowDownIcon, - ArrowUpIcon, - CheckIcon, - ChevronLeftIcon, - ChevronRightIcon, - CopyIcon, - DownloadIcon, - MoreHorizontalIcon, - PencilIcon, - RefreshCwIcon, - SquareIcon, -} from "lucide-react"; -import type { FC } from "react"; +"use client"; -export const Thread: FC = () => { - return ( - - - s.thread.isEmpty}> - - +import { type FC } from "react"; +import { AuiIf, ThreadPrimitive, useAuiState } from "@assistant-ui/react"; +import { ArrowDownIcon } from "lucide-react"; - - {() => } - - - - - - - - - ); -}; +import { ThreadHeader } from "./header"; +import { ThreadWelcome } from "./welcome"; +import { AssistantMessage, UserMessage } from "./messages"; +import { ThreadComposer } from "./composer"; +import { TooltipIconButton } from "./tooltip-icon-button"; const ThreadMessage: FC = () => { const role = useAuiState((s) => s.message.role); - const isEditing = useAuiState((s) => s.message.composer.isEditing); - if (isEditing) return ; if (role === "user") return ; return ; }; @@ -80,7 +22,7 @@ const ThreadScrollToBottom: FC = () => { @@ -88,281 +30,38 @@ const ThreadScrollToBottom: FC = () => { ); }; -const ThreadWelcome: FC = () => { +export const Thread: FC = () => { return ( -
-
-
-

- Hello there! -

-

- How can I help you today? + + + + + s.thread.isEmpty}> + + + + + {() => } + + + + + +

+ Právny AI Asistent môže robiť chyby. Overte dôležité informácie.

-
-
- -
+ + + ); -}; - -const ThreadSuggestions: FC = () => { - return ( -
- - {() => } - -
- ); -}; - -const ThreadSuggestionItem: FC = () => { - return ( -
- - - -
- ); -}; - -const Composer: FC = () => { - return ( - - -
- - - -
-
-
- ); -}; - -const ComposerAction: FC = () => { - return ( -
- - !s.thread.isRunning}> - - - - - - - s.thread.isRunning}> - - - - -
- ); -}; - -const MessageError: FC = () => { - return ( - - - - - - ); -}; - -const AssistantMessage: FC = () => { - return ( - -
- - {({ part }) => { - if (part.type === "text") return ; - if (part.type === "tool-call") - return part.toolUI ?? ; - return null; - }} - - -
- -
- - -
-
- ); -}; - -const AssistantActionBar: FC = () => { - return ( - - - - s.message.isCopied}> - - - !s.message.isCopied}> - - - - - - - - - - - - - - - - - - - - Export as Markdown - - - - - - ); -}; - -const UserMessage: FC = () => { - return ( - - - -
-
- -
-
- -
-
- - -
- ); -}; - -const UserActionBar: FC = () => { - return ( - - - - - - - - ); -}; - -const EditComposer: FC = () => { - return ( - - - -
- - - - - - -
-
-
- ); -}; - -const BranchPicker: FC = ({ - className, - ...rest -}) => { - return ( - - - - - - - - / - - - - - - - - ); -}; +}; \ No newline at end of file diff --git a/frontend/components/assistant-ui/welcome.tsx b/frontend/components/assistant-ui/welcome.tsx new file mode 100644 index 0000000..d87f589 --- /dev/null +++ b/frontend/components/assistant-ui/welcome.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { type FC } from "react"; + +const SUGGESTIONS = [ + { + icon: "🔍", + title: "Aké právne dáta", + description: "môže agent nájsť?", + prompt: "Aké právne dáta môžeš vyhľadať?", + }, + { + icon: "🚫", + title: "Čo agent nesmie", + description: "robiť alebo používať?", + prompt: "Čo nie si oprávnený robiť alebo použiť?", + }, + { + icon: "🤖", + title: "Detaily AI modelu", + description: "aký model používaš?", + prompt: "Aké sú detaily tvojho AI modelu?", + }, + { + icon: "📊", + title: "Zdroje dát", + description: "odkiaľ čerpáš informácie?", + prompt: "Aké dátové zdroje agent využíva?", + }, +]; + +const ThreadSuggestions: FC = () => { + const handleClick = (prompt: string) => { + const input = document.querySelector( + 'textarea[aria-label="Message input"]' + ) as HTMLTextAreaElement; + if (input) { + const nativeInputValueSetter = Object.getOwnPropertyDescriptor( + window.HTMLTextAreaElement.prototype, + "value" + )?.set; + nativeInputValueSetter?.call(input, prompt); + input.dispatchEvent(new Event("input", { bubbles: true })); + input.focus(); + } + }; + + return ( +
+ {SUGGESTIONS.map((s) => ( + + ))} +
+ ); +}; + +export const ThreadWelcome: FC = () => { + return ( +
+
+
+ Legal AI { + e.currentTarget.style.display = "none"; + e.currentTarget.parentElement!.innerHTML = `
⚖️
`; + }} + /> +
+
+

+ Legal AI Assistant +

+
+
+ +
+ ); +}; \ No newline at end of file diff --git a/frontend/components/ui/avatar.tsx b/frontend/components/ui/avatar.tsx deleted file mode 100644 index dfb285f..0000000 --- a/frontend/components/ui/avatar.tsx +++ /dev/null @@ -1,109 +0,0 @@ -"use client"; - -import * as React from "react"; -import { Avatar as AvatarPrimitive } from "radix-ui"; - -import { cn } from "@/lib/utils"; - -function Avatar({ - className, - size = "default", - ...props -}: React.ComponentProps & { - size?: "default" | "sm" | "lg"; -}) { - return ( - - ); -} - -function AvatarImage({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function AvatarFallback({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { - return ( - svg]:hidden", - "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", - "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", - className, - )} - {...props} - /> - ); -} - -function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ); -} - -function AvatarGroupCount({ - className, - ...props -}: React.ComponentProps<"div">) { - return ( -
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", - className, - )} - {...props} - /> - ); -} - -export { - Avatar, - AvatarImage, - AvatarFallback, - AvatarBadge, - AvatarGroup, - AvatarGroupCount, -}; diff --git a/frontend/components/ui/dialog.tsx b/frontend/components/ui/dialog.tsx deleted file mode 100644 index 944ef7d..0000000 --- a/frontend/components/ui/dialog.tsx +++ /dev/null @@ -1,158 +0,0 @@ -"use client"; - -import * as React from "react"; -import { XIcon } from "lucide-react"; -import { Dialog as DialogPrimitive } from "radix-ui"; - -import { cn } from "@/lib/utils"; -import { Button } from "@/components/ui/button"; - -function Dialog({ - ...props -}: React.ComponentProps) { - return ; -} - -function DialogTrigger({ - ...props -}: React.ComponentProps) { - return ; -} - -function DialogPortal({ - ...props -}: React.ComponentProps) { - return ; -} - -function DialogClose({ - ...props -}: React.ComponentProps) { - return ; -} - -function DialogOverlay({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function DialogContent({ - className, - children, - showCloseButton = true, - ...props -}: React.ComponentProps & { - showCloseButton?: boolean; -}) { - return ( - - - - {children} - {showCloseButton && ( - - - Close - - )} - - - ); -} - -function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { - return ( -
- ); -} - -function DialogFooter({ - className, - showCloseButton = false, - children, - ...props -}: React.ComponentProps<"div"> & { - showCloseButton?: boolean; -}) { - return ( -
- {children} - {showCloseButton && ( - - - - )} -
- ); -} - -function DialogTitle({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -function DialogDescription({ - className, - ...props -}: React.ComponentProps) { - return ( - - ); -} - -export { - Dialog, - DialogClose, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogOverlay, - DialogPortal, - DialogTitle, - DialogTrigger, -}; diff --git a/frontend/next.config.ts b/frontend/next.config.ts index e9ffa30..ca6c939 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + devIndicators: false, }; export default nextConfig; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3118d4c..09f6508 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -5340,9 +5340,9 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.7.tgz", - "integrity": "sha512-zwxwiQqexizSXFZV13zMiEtW1E3lv7RlUv+1f5FBiR4x7wFhEjm3aFTyYkZQWzyN08WnPdox015GoRH5D/E5YA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.8.tgz", + "integrity": "sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==", "license": "MIT", "engines": { "node": ">=18.0.0" diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 0000000..0882aaa Binary files /dev/null and b/frontend/public/logo.png differ