diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 0db1a8dd..00000000 --- a/docker-compose.yml +++ /dev/null @@ -1,11 +0,0 @@ -version: "3.8" - -services: - codegate-ui: - build: - context: . - dockerfile: Dockerfile - image: codegate-ui - ports: - - "3000:80" - container_name: codegate-ui diff --git a/dockerfile b/dockerfile deleted file mode 100644 index d6810cdf..00000000 --- a/dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM node:20.18.0-alpine AS builder - -WORKDIR /usr/src/app - -COPY package*.json tailwind.config.ts postcss.config.js tsconfig*.json components*.json vite.config.ts index.html ./ - -RUN npm install - -COPY ./src ./src -COPY ./public ./public - -RUN npm run build - -FROM nginx:stable-alpine - -COPY --from=builder /usr/src/app/dist /usr/share/nginx/html - -EXPOSE 80 - -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index 5cb96487..00000000 --- a/nginx.conf +++ /dev/null @@ -1,28 +0,0 @@ -server { - listen 80; - - server_name localhost; - - root /usr/share/nginx/html; - - index index.html; - - # Serve the front-end application for all unmatched routes - location / { - try_files $uri /index.html; - } - - # Serve static files for assets - location /assets/ { - root /usr/share/nginx/html; - expires max; - add_header Cache-Control public; - } - - # Serve certificate file directly - location /certificates/ { - root /usr/share/nginx/html; - default_type application/octet-stream; - autoindex on; - } -} diff --git a/package-lock.json b/package-lock.json index 569fdde2..cc331f9c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,6 +20,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "highlight.js": "^11.11.1", "lucide-react": "^0.462.0", "prismjs": "^1.29.0", "react": "19.0.0", @@ -4562,11 +4563,12 @@ } }, "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", "engines": { - "node": "*" + "node": ">=12.0.0" } }, "node_modules/highlightjs-vue": { @@ -5238,6 +5240,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/lowlight/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -6901,6 +6912,15 @@ "react": ">= 0.14.0" } }, + "node_modules/react-syntax-highlighter/node_modules/highlight.js": { + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", + "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/package.json b/package.json index e1170da5..a9915d78 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^4.1.0", + "highlight.js": "^11.11.1", "lucide-react": "^0.462.0", "prismjs": "^1.29.0", "react": "19.0.0", diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index 6a68169c..597c973e 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -27,7 +27,7 @@ export function Chat() {
{(chat?.question_answers ?? []).map(({ question, answer }, index) => ( -
+
diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 622b1ca8..6245a312 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -65,13 +65,15 @@ const wrapObjectOutput = (input: string | MaliciousPkgType | null) => { const isObject = /\{"/.test(input); if (isObject) { return ( -
+      
         {input}
       
); } return ( - {input} + + {input} + ); }; @@ -119,7 +121,7 @@ export function Dashboard() { setSearchParams(searchParams); toggleMaliciousFilter(isChecked); }, - [setSearchParams, setSearch, searchParams, toggleMaliciousFilter] + [setSearchParams, setSearch, searchParams, toggleMaliciousFilter], ); const handleSearch = useCallback( @@ -135,7 +137,7 @@ export function Dashboard() { } setSearchParams(searchParams); }, - [searchParams, setSearch, setSearchParams, toggleMaliciousFilter] + [searchParams, setSearch, setSearchParams, toggleMaliciousFilter], ); return ( diff --git a/src/components/Markdown.tsx b/src/components/Markdown.tsx index 9285ea88..9c561894 100644 --- a/src/components/Markdown.tsx +++ b/src/components/Markdown.tsx @@ -4,6 +4,32 @@ import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"; import { cn } from "@/lib/utils"; import { CopyToClipboard } from "./CopyToClipboard"; +import hljs from "highlight.js"; + +const LANGUAGES_SUBSET_DETECTION = [ + "c", + "cpp", + "csharp", + "css", + "elixir", + "go", + "groovy", + "haskell", + "html", + "java", + "javascript", + "json", + "kotlin", + "markdown", + "php", + "python", + "ruby", + "rust", + "scala", + "sql", + "typescript", + "yaml", +]; interface Props { children: string; @@ -12,85 +38,58 @@ interface Props { const customStyle = { ...oneDark, + 'code[class*="language-"]': { + ...oneDark['code[class*="language-"]'], + background: "none", + }, 'pre[class*="language-"]': { ...oneDark['pre[class*="language-"]'], - whiteSpace: "pre-wrap", background: "#1a1b26", padding: "1.5rem", borderRadius: "0.5rem", - margin: "1.5rem 0", - fontSize: "10px", - width: "80%", // Ensure the block takes full width + width: "100%", position: "relative", + boxSizing: "border-box", }, }; - export function Markdown({ children, className = "" }: Props) { + SyntaxHighlighter.supportedLanguages = LANGUAGES_SUBSET_DETECTION; return ( + const language = match ? match[1] : detectedLanguage; + return ( +
{String(children).replace(/\n$/, "")} - + {match && ( + + )}
- ) : ( - - {children} - ); }, p({ children }) { return ( -

+

{children}

); diff --git a/src/components/ui/chat/chat-bubble.tsx b/src/components/ui/chat/chat-bubble.tsx index b9d20ecc..eed61fd3 100644 --- a/src/components/ui/chat/chat-bubble.tsx +++ b/src/components/ui/chat/chat-bubble.tsx @@ -1,202 +1,202 @@ -import * as React from "react"; -import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils"; -import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; -import MessageLoading from "./message-loading"; -import { ButtonProps, Button } from "../button"; - -// ChatBubble -const chatBubbleVariant = cva( - "flex gap-2 max-w-[60%] items-end relative group", - { - variants: { - variant: { - received: "self-start", - sent: "self-end flex-row-reverse", - }, - layout: { - default: "", - ai: "max-w-full w-full items-center", - }, - }, - defaultVariants: { - variant: "received", - layout: "default", - }, - } -); - -interface ChatBubbleProps - extends React.HTMLAttributes, - VariantProps {} - -const ChatBubble = React.forwardRef( - ({ className, variant, layout, children, ...props }, ref) => ( -
- {React.Children.map(children, (child) => - React.isValidElement(child) && typeof child.type !== "string" - ? React.cloneElement(child, { - variant, - layout, - } as React.ComponentProps) - : child - )} -
- ) -); -ChatBubble.displayName = "ChatBubble"; - -// ChatBubbleAvatar -interface ChatBubbleAvatarProps { - src?: string; - fallback?: string; - className?: string; -} - -const ChatBubbleAvatar: React.FC = ({ - src, - fallback, - className, -}) => ( - - - {fallback} - -); - -// ChatBubbleMessage -const chatBubbleMessageVariants = cva("p-4", { - variants: { - variant: { - received: - "bg-secondary text-secondary-foreground rounded-r-lg rounded-tl-lg", - sent: "bg-primary text-primary-foreground rounded-l-lg rounded-tr-lg", - }, - layout: { - default: "", - ai: "border-t w-full rounded-none bg-transparent", - }, - }, - defaultVariants: { - variant: "received", - layout: "default", - }, -}); - -interface ChatBubbleMessageProps - extends React.HTMLAttributes, - VariantProps { - isLoading?: boolean; -} - -const ChatBubbleMessage = React.forwardRef< - HTMLDivElement, - ChatBubbleMessageProps ->( - ( - { className, variant, layout, isLoading = false, children, ...props }, - ref - ) => ( -
- {isLoading ? ( -
- -
- ) : ( - children - )} -
- ) -); -ChatBubbleMessage.displayName = "ChatBubbleMessage"; - -// ChatBubbleTimestamp -interface ChatBubbleTimestampProps - extends React.HTMLAttributes { - timestamp: string; -} - -const ChatBubbleTimestamp: React.FC = ({ - timestamp, - className, - ...props -}) => ( -
- {timestamp} -
-); - -// ChatBubbleAction -type ChatBubbleActionProps = ButtonProps & { - icon: React.ReactNode; -}; - -const ChatBubbleAction: React.FC = ({ - icon, - onClick, - className, - variant = "ghost", - size = "icon", - ...props -}) => ( - -); - -interface ChatBubbleActionWrapperProps - extends React.HTMLAttributes { - variant?: "sent" | "received"; - className?: string; -} - -const ChatBubbleActionWrapper = React.forwardRef< - HTMLDivElement, - ChatBubbleActionWrapperProps ->(({ variant, className, children, ...props }, ref) => ( -
- {children} -
-)); -ChatBubbleActionWrapper.displayName = "ChatBubbleActionWrapper"; - -export { - ChatBubble, - ChatBubbleAvatar, - ChatBubbleMessage, - ChatBubbleTimestamp, - chatBubbleVariant, - chatBubbleMessageVariants, - ChatBubbleAction, - ChatBubbleActionWrapper, -}; +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/lib/utils"; +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; +import MessageLoading from "./message-loading"; +import { ButtonProps, Button } from "../button"; + +// ChatBubble +const chatBubbleVariant = cva( + "flex gap-2 max-w-[60%] items-end relative group", + { + variants: { + variant: { + received: "self-start", + sent: "self-end flex-row-reverse", + }, + layout: { + default: "", + ai: "max-w-full w-full items-center", + }, + }, + defaultVariants: { + variant: "received", + layout: "default", + }, + }, +); + +interface ChatBubbleProps + extends React.HTMLAttributes, + VariantProps {} + +const ChatBubble = React.forwardRef( + ({ className, variant, layout, children, ...props }, ref) => ( +
+ {React.Children.map(children, (child) => + React.isValidElement(child) && typeof child.type !== "string" + ? React.cloneElement(child, { + variant, + layout, + } as React.ComponentProps) + : child, + )} +
+ ), +); +ChatBubble.displayName = "ChatBubble"; + +// ChatBubbleAvatar +interface ChatBubbleAvatarProps { + src?: string; + fallback?: string; + className?: string; +} + +const ChatBubbleAvatar: React.FC = ({ + src, + fallback, + className, +}) => ( + + + {fallback} + +); + +// ChatBubbleMessage +const chatBubbleMessageVariants = cva("p-4", { + variants: { + variant: { + received: + "bg-secondary text-secondary-foreground rounded-r-lg rounded-tl-lg", + sent: "bg-primary text-primary-foreground rounded-l-lg rounded-tr-lg", + }, + layout: { + default: "", + ai: "border-t w-full rounded-none bg-transparent", + }, + }, + defaultVariants: { + variant: "received", + layout: "default", + }, +}); + +interface ChatBubbleMessageProps + extends React.HTMLAttributes, + VariantProps { + isLoading?: boolean; +} + +const ChatBubbleMessage = React.forwardRef< + HTMLDivElement, + ChatBubbleMessageProps +>( + ( + { className, variant, layout, isLoading = false, children, ...props }, + ref, + ) => ( +
+ {isLoading ? ( +
+ +
+ ) : ( + children + )} +
+ ), +); +ChatBubbleMessage.displayName = "ChatBubbleMessage"; + +// ChatBubbleTimestamp +interface ChatBubbleTimestampProps + extends React.HTMLAttributes { + timestamp: string; +} + +const ChatBubbleTimestamp: React.FC = ({ + timestamp, + className, + ...props +}) => ( +
+ {timestamp} +
+); + +// ChatBubbleAction +type ChatBubbleActionProps = ButtonProps & { + icon: React.ReactNode; +}; + +const ChatBubbleAction: React.FC = ({ + icon, + onClick, + className, + variant = "ghost", + size = "icon", + ...props +}) => ( + +); + +interface ChatBubbleActionWrapperProps + extends React.HTMLAttributes { + variant?: "sent" | "received"; + className?: string; +} + +const ChatBubbleActionWrapper = React.forwardRef< + HTMLDivElement, + ChatBubbleActionWrapperProps +>(({ variant, className, children, ...props }, ref) => ( +
+ {children} +
+)); +ChatBubbleActionWrapper.displayName = "ChatBubbleActionWrapper"; + +export { + ChatBubble, + ChatBubbleAvatar, + ChatBubbleMessage, + ChatBubbleTimestamp, + chatBubbleVariant, + chatBubbleMessageVariants, + ChatBubbleAction, + ChatBubbleActionWrapper, +};