diff --git a/frontend/app/(app)/code/[id]/page.tsx b/frontend/app/(app)/code/[id]/page.tsx index 27bf89e..a959fd4 100644 --- a/frontend/app/(app)/code/[id]/page.tsx +++ b/frontend/app/(app)/code/[id]/page.tsx @@ -51,7 +51,11 @@ const getSharedUsers = async (usersToSandboxes: UsersToSandboxes[]) => { } ) const userData: User = await userRes.json() - return { id: userData.id, name: userData.name, avatarUrl: userData.avatarUrl } + return { + id: userData.id, + name: userData.name, + avatarUrl: userData.avatarUrl, + } }) ) @@ -94,7 +98,9 @@ export default async function CodePage({ params }: { params: { id: string } }) {
diff --git a/frontend/app/(app)/layout.tsx b/frontend/app/(app)/layout.tsx index f4a5a5b..7c236a3 100644 --- a/frontend/app/(app)/layout.tsx +++ b/frontend/app/(app)/layout.tsx @@ -1,7 +1,7 @@ import { User } from "@/lib/types" +import { generateUniqueUsername } from "@/lib/username-generator" import { currentUser } from "@clerk/nextjs" import { redirect } from "next/navigation" -import { generateUniqueUsername } from "@/lib/username-generator"; export default async function AppAuthLayout({ children, @@ -27,22 +27,24 @@ export default async function AppAuthLayout({ if (!dbUserJSON.id) { // Try to get GitHub username if available const githubUsername = user.externalAccounts.find( - account => account.provider === "github" - )?.username; + (account) => account.provider === "github" + )?.username - const username = githubUsername || await generateUniqueUsername(async (username) => { - // Check if username exists in database - const userCheck = await fetch( - `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user/check-username?username=${username}`, - { - headers: { - Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, - }, - } - ) - const exists = await userCheck.json() - return exists.exists - }); + const username = + githubUsername || + (await generateUniqueUsername(async (username) => { + // Check if username exists in database + const userCheck = await fetch( + `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user/check-username?username=${username}`, + { + headers: { + Authorization: `${process.env.NEXT_PUBLIC_WORKERS_KEY}`, + }, + } + ) + const exists = await userCheck.json() + return exists.exists + })) const res = await fetch( `${process.env.NEXT_PUBLIC_DATABASE_WORKER_URL}/api/user`, @@ -64,11 +66,11 @@ export default async function AppAuthLayout({ ) if (!res.ok) { - const error = await res.text(); - console.error("Failed to create user:", error); + const error = await res.text() + console.error("Failed to create user:", error) } else { - const data = await res.json(); - console.log("User created successfully:", data); + const data = await res.json() + console.log("User created successfully:", data) } } diff --git a/frontend/app/layout.tsx b/frontend/app/layout.tsx index 494079e..57f5c17 100644 --- a/frontend/app/layout.tsx +++ b/frontend/app/layout.tsx @@ -1,7 +1,7 @@ import { Toaster } from "@/components/ui/sonner" import { ThemeProvider } from "@/components/ui/theme-provider" import { PreviewProvider } from "@/context/PreviewContext" -import { SocketProvider } from '@/context/SocketContext' +import { SocketProvider } from "@/context/SocketContext" import { ClerkProvider } from "@clerk/nextjs" import { Analytics } from "@vercel/analytics/react" import { GeistMono } from "geist/font/mono" diff --git a/frontend/components.json b/frontend/components.json index 05f9e72..bd85d8a 100644 --- a/frontend/components.json +++ b/frontend/components.json @@ -14,4 +14,4 @@ "components": "@/components", "utils": "@/lib/utils" } -} \ No newline at end of file +} diff --git a/frontend/components/dashboard/shared.tsx b/frontend/components/dashboard/shared.tsx index 85ce3d2..b65c89f 100644 --- a/frontend/components/dashboard/shared.tsx +++ b/frontend/components/dashboard/shared.tsx @@ -6,12 +6,12 @@ import { TableHeader, TableRow, } from "@/components/ui/table" +import { projectTemplates } from "@/lib/data" import { ChevronRight } from "lucide-react" import Image from "next/image" import Link from "next/link" import Avatar from "../ui/avatar" import Button from "../ui/customButton" -import { projectTemplates } from "@/lib/data" export default function DashboardSharedWithMe({ shared, @@ -47,7 +47,8 @@ export default function DashboardSharedWithMe({ p.id === sandbox.type)?.icon ?? "/project-icons/node.svg" + projectTemplates.find((p) => p.id === sandbox.type) + ?.icon ?? "/project-icons/node.svg" } width={20} height={20} @@ -58,7 +59,7 @@ export default function DashboardSharedWithMe({
- { if (textareaRef.current) { - textareaRef.current.style.height = 'auto' - textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px' + textareaRef.current.style.height = "auto" + textareaRef.current.style.height = textareaRef.current.scrollHeight + "px" } }, [input]) @@ -40,7 +38,11 @@ export default function ChatInput({ e.preventDefault() handleSend(false) } - } else if (e.key === "Backspace" && input === "" && contextTabs.length > 0) { + } else if ( + e.key === "Backspace" && + input === "" && + contextTabs.length > 0 + ) { e.preventDefault() // Remove the last context tab const lastTab = contextTabs[contextTabs.length - 1] @@ -51,89 +53,92 @@ export default function ChatInput({ // Handle paste events for image and code const handlePaste = async (e: React.ClipboardEvent) => { // Handle image paste - const items = Array.from(e.clipboardData.items); + const items = Array.from(e.clipboardData.items) for (const item of items) { - if (item.type.startsWith('image/')) { - e.preventDefault(); - - const file = item.getAsFile(); - if (!file) continue; + if (item.type.startsWith("image/")) { + e.preventDefault() + + const file = item.getAsFile() + if (!file) continue try { // Convert image to base64 string for context tab title and timestamp - const reader = new FileReader(); + const reader = new FileReader() reader.onload = () => { - const base64String = reader.result as string; + const base64String = reader.result as string addContextTab( "image", - `Image ${new Date().toLocaleTimeString('en-US', { - hour12: true, - hour: '2-digit', - minute: '2-digit' - }).replace(/(\d{2}):(\d{2})/, '$1:$2')}`, + `Image ${new Date() + .toLocaleTimeString("en-US", { + hour12: true, + hour: "2-digit", + minute: "2-digit", + }) + .replace(/(\d{2}):(\d{2})/, "$1:$2")}`, base64String - ); - }; - reader.readAsDataURL(file); + ) + } + reader.readAsDataURL(file) } catch (error) { - console.error('Error processing pasted image:', error); + console.error("Error processing pasted image:", error) } - return; + return } } - - // Get text from clipboard - const text = e.clipboardData.getData('text'); + + // Get text from clipboard + const text = e.clipboardData.getData("text") // If text doesn't contain newlines or doesn't look like code, let it paste normally - if (!text || !text.includes('\n') || !looksLikeCode(text)) { - return; + if (!text || !text.includes("\n") || !looksLikeCode(text)) { + return } - e.preventDefault(); - const editor = editorRef.current; - const currentSelection = editor?.getSelection(); - const lines = text.split('\n'); + e.preventDefault() + const editor = editorRef.current + const currentSelection = editor?.getSelection() + const lines = text.split("\n") // TODO: FIX THIS: even when i paste the outside code, it shows the active file name,it works when no tabs are open, just does not work when the tab is open - + // If selection exists in editor, use file name and line numbers if (currentSelection && !currentSelection.isEmpty()) { addContextTab( "code", `${activeFileName} (${currentSelection.startLineNumber}-${currentSelection.endLineNumber})`, text, - { start: currentSelection.startLineNumber, end: currentSelection.endLineNumber } - ); - return; + { + start: currentSelection.startLineNumber, + end: currentSelection.endLineNumber, + } + ) + return } - + // If we have stored line range from a copy operation in the editor if (lastCopiedRangeRef.current) { - const range = lastCopiedRangeRef.current; + const range = lastCopiedRangeRef.current addContextTab( "code", `${activeFileName} (${range.startLine}-${range.endLine})`, text, { start: range.startLine, end: range.endLine } - ); - return; + ) + return } - + // For code pasted from outside the editor - addContextTab( - "code", - `Pasted Code (1-${lines.length})`, - text, - { start: 1, end: lines.length } - ); - }; + addContextTab("code", `Pasted Code (1-${lines.length})`, text, { + start: 1, + end: lines.length, + }) + } // Handle image upload from local machine via input const handleImageUpload = () => { - const input = document.createElement('input') - input.type = 'file' - input.accept = 'image/*' + const input = document.createElement("input") + input.type = "file" + input.accept = "image/*" input.onchange = (e) => { const file = (e.target as HTMLInputElement).files?.[0] if (file) onImageUpload(file) @@ -155,14 +160,16 @@ export default function ChatInput({ // Handle file upload from local machine via input const handleFileUpload = () => { - const input = document.createElement('input') - input.type = 'file' - input.accept = '.txt,.md,.csv,.json,.js,.ts,.html,.css,.pdf' + const input = document.createElement("input") + input.type = "file" + input.accept = ".txt,.md,.csv,.json,.js,.ts,.html,.css,.pdf" input.onchange = (e) => { const file = (e.target as HTMLInputElement).files?.[0] if (file) { if (!(file.type in ALLOWED_FILE_TYPES)) { - alert('Unsupported file type. Please upload text, code, or PDF files.') + alert( + "Unsupported file type. Please upload text, code, or PDF files." + ) return } @@ -223,17 +230,16 @@ export default function ChatInput({ File {/* Render image upload button */} -
) } - diff --git a/frontend/components/editor/AIChat/ChatMessage.tsx b/frontend/components/editor/AIChat/ChatMessage.tsx index ef8ec0c..f64679e 100644 --- a/frontend/components/editor/AIChat/ChatMessage.tsx +++ b/frontend/components/editor/AIChat/ChatMessage.tsx @@ -3,9 +3,9 @@ import React, { useState } from "react" import ReactMarkdown from "react-markdown" import remarkGfm from "remark-gfm" import { Button } from "../../ui/button" -import { copyToClipboard, stringifyContent } from "./lib/chatUtils" import ContextTabs from "./ContextTabs" -import { createMarkdownComponents } from './lib/markdownComponents' +import { copyToClipboard, stringifyContent } from "./lib/chatUtils" +import { createMarkdownComponents } from "./lib/markdownComponents" import { MessageProps } from "./types" export default function ChatMessage({ @@ -14,7 +14,6 @@ export default function ChatMessage({ setIsContextExpanded, socket, }: MessageProps) { - // State for expanded message index const [expandedMessageIndex, setExpandedMessageIndex] = useState< number | null @@ -23,7 +22,7 @@ export default function ChatMessage({ // State for copied text const [copiedText, setCopiedText] = useState(null) - // Render copy button for text content + // Render copy button for text content const renderCopyButton = (text: any) => ( @@ -143,20 +145,23 @@ export default function ContextTabs({ {previewTab && (
{previewTab.type === "image" ? ( - {previewTab.name} - ) : previewTab.lineRange && ( - <> -
- Lines {previewTab.lineRange.start}-{previewTab.lineRange.end} -
-
-                  {previewTab.content}
-                
- + ) : ( + previewTab.lineRange && ( + <> +
+ Lines {previewTab.lineRange.start}- + {previewTab.lineRange.end} +
+
+                    {previewTab.content}
+                  
+ + ) )} {/* Render file context tab */} {previewTab.type === "file" && ( @@ -169,4 +174,4 @@ export default function ContextTabs({
) -} \ No newline at end of file +} diff --git a/frontend/components/editor/AIChat/index.tsx b/frontend/components/editor/AIChat/index.tsx index 444e576..664928f 100644 --- a/frontend/components/editor/AIChat/index.tsx +++ b/frontend/components/editor/AIChat/index.tsx @@ -1,14 +1,14 @@ +import { useSocket } from "@/context/SocketContext" +import { TFile } from "@/lib/types" import { X } from "lucide-react" +import { nanoid } from "nanoid" import { useEffect, useRef, useState } from "react" import LoadingDots from "../../ui/LoadingDots" import ChatInput from "./ChatInput" import ChatMessage from "./ChatMessage" import ContextTabs from "./ContextTabs" import { handleSend, handleStopGeneration } from "./lib/chatUtils" -import { nanoid } from 'nanoid' -import { TFile } from "@/lib/types" -import { useSocket } from "@/context/SocketContext" -import { Message, ContextTab, AIChatProps } from './types' +import { AIChatProps, ContextTab, Message } from "./types" export default function AIChat({ activeFileContent, @@ -56,47 +56,54 @@ export default function AIChat({ } // Add context tab to context tabs - const addContextTab = (type: string, name: string, content: string, lineRange?: { start: number; end: number }) => { + const addContextTab = ( + type: string, + name: string, + content: string, + lineRange?: { start: number; end: number } + ) => { const newTab = { id: nanoid(), type: type as "file" | "code" | "image", name, content, - lineRange + lineRange, } - setContextTabs(prev => [...prev, newTab]) + setContextTabs((prev) => [...prev, newTab]) } // Remove context tab from context tabs const removeContextTab = (id: string) => { - setContextTabs(prev => prev.filter(tab => tab.id !== id)) + setContextTabs((prev) => prev.filter((tab) => tab.id !== id)) } // Add file to context tabs const handleAddFile = (tab: ContextTab) => { - setContextTabs(prev => [...prev, tab]) + setContextTabs((prev) => [...prev, tab]) } // Format code content to remove starting and ending code block markers if they exist const formatCodeContent = (content: string) => { - return content.replace(/^```[\w-]*\n/, '').replace(/\n```$/, '') + return content.replace(/^```[\w-]*\n/, "").replace(/\n```$/, "") } // Get combined context from context tabs const getCombinedContext = () => { - if (contextTabs.length === 0) return '' - - return contextTabs.map(tab => { - if (tab.type === 'file') { - const fileExt = tab.name.split('.').pop() || 'txt' - const cleanContent = formatCodeContent(tab.content) - return `File ${tab.name}:\n\`\`\`${fileExt}\n${cleanContent}\n\`\`\`` - } else if (tab.type === 'code') { - const cleanContent = formatCodeContent(tab.content) - return `Code from ${tab.name}:\n\`\`\`typescript\n${cleanContent}\n\`\`\`` - } - return `${tab.name}:\n${tab.content}` - }).join('\n\n') + if (contextTabs.length === 0) return "" + + return contextTabs + .map((tab) => { + if (tab.type === "file") { + const fileExt = tab.name.split(".").pop() || "txt" + const cleanContent = formatCodeContent(tab.content) + return `File ${tab.name}:\n\`\`\`${fileExt}\n${cleanContent}\n\`\`\`` + } else if (tab.type === "code") { + const cleanContent = formatCodeContent(tab.content) + return `Code from ${tab.name}:\n\`\`\`typescript\n${cleanContent}\n\`\`\`` + } + return `${tab.name}:\n${tab.content}` + }) + .join("\n\n") } // Handle sending message with context @@ -120,9 +127,9 @@ export default function AIChat({ // Set context for the chat const setContext = ( - context: string | null, - name: string, - range?: { start: number, end: number } + context: string | null, + name: string, + range?: { start: number; end: number } ) => { if (!context) { setContextTabs([]) @@ -130,7 +137,7 @@ export default function AIChat({ } // Always add a new tab instead of updating existing ones - addContextTab('code', name, context, range) + addContextTab("code", name, context, range) } return ( @@ -156,7 +163,7 @@ export default function AIChat({ className="flex-grow overflow-y-auto p-4 space-y-4" > {messages.map((message, messageIndex) => ( - // Render chat message component for each message + // Render chat message component for each message { socket?.emit("getFile", { fileId: file.id }, (response: string) => { - const fileExt = file.name.split('.').pop() || 'txt' + const fileExt = file.name.split(".").pop() || "txt" const formattedContent = `\`\`\`${fileExt}\n${response}\n\`\`\`` - addContextTab('file', file.name, formattedContent) + addContextTab("file", file.name, formattedContent) if (textareaRef.current) { textareaRef.current.focus() } @@ -210,9 +217,9 @@ export default function AIChat({ }} lastCopiedRangeRef={lastCopiedRangeRef} activeFileName={activeFileName} - contextTabs={contextTabs.map(tab => ({ + contextTabs={contextTabs.map((tab) => ({ ...tab, - title: tab.id + title: tab.id, }))} onRemoveTab={removeContextTab} /> diff --git a/frontend/components/editor/AIChat/lib/chatUtils.ts b/frontend/components/editor/AIChat/lib/chatUtils.ts index d02c91e..950096f 100644 --- a/frontend/components/editor/AIChat/lib/chatUtils.ts +++ b/frontend/components/editor/AIChat/lib/chatUtils.ts @@ -1,6 +1,6 @@ import React from "react" -// Stringify content for chat message component +// Stringify content for chat message component export const stringifyContent = ( content: any, seen = new WeakSet() @@ -66,19 +66,19 @@ export const stringifyContent = ( return String(content) } -// Copy to clipboard for chat message component +// Copy to clipboard for chat message component export const copyToClipboard = ( text: string, setCopiedText: (text: string | null) => void ) => { - // Copy text to clipboard for chat message component + // Copy text to clipboard for chat message component navigator.clipboard.writeText(text).then(() => { setCopiedText(text) setTimeout(() => setCopiedText(null), 2000) }) } -// Handle send for chat message component +// Handle send for chat message component export const handleSend = async ( input: string, context: string | null, @@ -92,24 +92,26 @@ export const handleSend = async ( activeFileContent: string ) => { // Return if input is empty and context is null - if (input.trim() === "" && !context) return + if (input.trim() === "" && !context) return - // Get timestamp for chat message component - const timestamp = new Date().toLocaleTimeString('en-US', { - hour12: true, - hour: '2-digit', - minute: '2-digit' - }).replace(/(\d{2}):(\d{2})/, '$1:$2') + // Get timestamp for chat message component + const timestamp = new Date() + .toLocaleTimeString("en-US", { + hour12: true, + hour: "2-digit", + minute: "2-digit", + }) + .replace(/(\d{2}):(\d{2})/, "$1:$2") - // Create user message for chat message component + // Create user message for chat message component const userMessage = { role: "user" as const, content: input, context: context || undefined, - timestamp: timestamp + timestamp: timestamp, } - // Update messages for chat message component + // Update messages for chat message component const updatedMessages = [...messages, userMessage] setMessages(updatedMessages) setInput("") @@ -120,13 +122,13 @@ export const handleSend = async ( abortControllerRef.current = new AbortController() try { - // Create anthropic messages for chat message component + // Create anthropic messages for chat message component const anthropicMessages = updatedMessages.map((msg) => ({ role: msg.role === "user" ? "human" : "assistant", content: msg.content, })) - // Fetch AI response for chat message component + // Fetch AI response for chat message component const response = await fetch( `${process.env.NEXT_PUBLIC_AI_WORKER_URL}/api`, { @@ -148,19 +150,19 @@ export const handleSend = async ( throw new Error("Failed to get AI response") } - // Get reader for chat message component + // Get reader for chat message component const reader = response.body?.getReader() const decoder = new TextDecoder() const assistantMessage = { role: "assistant" as const, content: "" } setMessages([...updatedMessages, assistantMessage]) setIsLoading(false) - // Initialize buffer for chat message component + // Initialize buffer for chat message component let buffer = "" const updateInterval = 100 let lastUpdateTime = Date.now() - // Read response from reader for chat message component + // Read response from reader for chat message component if (reader) { while (true) { const { done, value } = await reader.read() @@ -179,7 +181,7 @@ export const handleSend = async ( } } - // Update messages for chat message component + // Update messages for chat message component setMessages((prev) => { const updatedMessages = [...prev] const lastMessage = updatedMessages[updatedMessages.length - 1] @@ -188,7 +190,7 @@ export const handleSend = async ( }) } } catch (error: any) { - // Handle abort error for chat message component + // Handle abort error for chat message component if (error.name === "AbortError") { console.log("Generation aborted") } else { @@ -206,7 +208,7 @@ export const handleSend = async ( } } -// Handle stop generation for chat message component +// Handle stop generation for chat message component export const handleStopGeneration = ( abortControllerRef: React.MutableRefObject ) => { @@ -215,21 +217,21 @@ export const handleStopGeneration = ( } } -// Check if text looks like code for chat message component +// Check if text looks like code for chat message component export const looksLikeCode = (text: string): boolean => { const codeIndicators = [ - /^import\s+/m, // import statements - /^function\s+/m, // function declarations - /^class\s+/m, // class declarations - /^const\s+/m, // const declarations - /^let\s+/m, // let declarations - /^var\s+/m, // var declarations - /[{}\[\]();]/, // common code syntax - /^\s*\/\//m, // comments - /^\s*\/\*/m, // multi-line comments - /=>/, // arrow functions - /^export\s+/m, // export statements - ]; + /^import\s+/m, // import statements + /^function\s+/m, // function declarations + /^class\s+/m, // class declarations + /^const\s+/m, // const declarations + /^let\s+/m, // let declarations + /^var\s+/m, // var declarations + /[{}\[\]();]/, // common code syntax + /^\s*\/\//m, // comments + /^\s*\/\*/m, // multi-line comments + /=>/, // arrow functions + /^export\s+/m, // export statements + ] - return codeIndicators.some(pattern => pattern.test(text)); -}; + return codeIndicators.some((pattern) => pattern.test(text)) +} diff --git a/frontend/components/editor/AIChat/lib/ignored-paths.ts b/frontend/components/editor/AIChat/lib/ignored-paths.ts index 75d8f2b..0e6da33 100644 --- a/frontend/components/editor/AIChat/lib/ignored-paths.ts +++ b/frontend/components/editor/AIChat/lib/ignored-paths.ts @@ -1,102 +1,102 @@ -// Ignore certain folders and files from the file tree +// Ignore certain folders and files from the file tree export const ignoredFolders = [ - // Package managers - 'node_modules', - 'venv', - '.env', - 'env', - '.venv', - 'virtualenv', - 'pip-wheel-metadata', - - // Build outputs - '.next', - 'dist', - 'build', - 'out', - '__pycache__', - '.webpack', - '.serverless', - 'storybook-static', - - // Version control - '.git', - '.svn', - '.hg', // Mercurial - - // Cache and temp files - '.cache', - 'coverage', - 'tmp', - '.temp', - '.npm', - '.pnpm', - '.yarn', - '.eslintcache', - '.stylelintcache', - - // IDE specific - '.idea', - '.vscode', - '.vs', - '.sublime', - - // Framework specific - '.streamlit', - '.next', - 'static', - '.pytest_cache', - '.nuxt', - '.docusaurus', - '.remix', - '.parcel-cache', - 'public/build', // Remix/Rails - '.turbo', // Turborepo - - // Logs - 'logs', - '*.log', - 'npm-debug.log*', - 'yarn-debug.log*', - 'yarn-error.log*', - 'pnpm-debug.log*', - ] as const; - - export const ignoredFiles = [ - '.DS_Store', - '.env.local', - '.env.development', - '.env.production', - '.env.test', - '.env*.local', - '.gitignore', - '.npmrc', - '.yarnrc', - '.editorconfig', - '.prettierrc', - '.eslintrc', - '.browserslistrc', - 'tsconfig.tsbuildinfo', - '*.pyc', - '*.pyo', - '*.pyd', - '*.so', - '*.dll', - '*.dylib', - '*.class', - '*.exe', - 'package-lock.json', - 'yarn.lock', - 'pnpm-lock.yaml', - 'composer.lock', - 'poetry.lock', - 'Gemfile.lock', - '*.min.js', - '*.min.css', - '*.map', - '*.chunk.*', - '*.hot-update.*', - '.vercel', - '.netlify' - ] as const; \ No newline at end of file + // Package managers + "node_modules", + "venv", + ".env", + "env", + ".venv", + "virtualenv", + "pip-wheel-metadata", + + // Build outputs + ".next", + "dist", + "build", + "out", + "__pycache__", + ".webpack", + ".serverless", + "storybook-static", + + // Version control + ".git", + ".svn", + ".hg", // Mercurial + + // Cache and temp files + ".cache", + "coverage", + "tmp", + ".temp", + ".npm", + ".pnpm", + ".yarn", + ".eslintcache", + ".stylelintcache", + + // IDE specific + ".idea", + ".vscode", + ".vs", + ".sublime", + + // Framework specific + ".streamlit", + ".next", + "static", + ".pytest_cache", + ".nuxt", + ".docusaurus", + ".remix", + ".parcel-cache", + "public/build", // Remix/Rails + ".turbo", // Turborepo + + // Logs + "logs", + "*.log", + "npm-debug.log*", + "yarn-debug.log*", + "yarn-error.log*", + "pnpm-debug.log*", +] as const + +export const ignoredFiles = [ + ".DS_Store", + ".env.local", + ".env.development", + ".env.production", + ".env.test", + ".env*.local", + ".gitignore", + ".npmrc", + ".yarnrc", + ".editorconfig", + ".prettierrc", + ".eslintrc", + ".browserslistrc", + "tsconfig.tsbuildinfo", + "*.pyc", + "*.pyo", + "*.pyd", + "*.so", + "*.dll", + "*.dylib", + "*.class", + "*.exe", + "package-lock.json", + "yarn.lock", + "pnpm-lock.yaml", + "composer.lock", + "poetry.lock", + "Gemfile.lock", + "*.min.js", + "*.min.css", + "*.map", + "*.chunk.*", + "*.hot-update.*", + ".vercel", + ".netlify", +] as const diff --git a/frontend/components/editor/AIChat/lib/markdownComponents.tsx b/frontend/components/editor/AIChat/lib/markdownComponents.tsx index 03009ae..c5cbbf3 100644 --- a/frontend/components/editor/AIChat/lib/markdownComponents.tsx +++ b/frontend/components/editor/AIChat/lib/markdownComponents.tsx @@ -1,21 +1,26 @@ +import { CornerUpLeft } from "lucide-react" import { Components } from "react-markdown" import { Prism as SyntaxHighlighter } from "react-syntax-highlighter" import { vscDarkPlus } from "react-syntax-highlighter/dist/esm/styles/prism" import { Button } from "../../../ui/button" -import { CornerUpLeft } from "lucide-react" import { stringifyContent } from "./chatUtils" -// Create markdown components for chat message component +// Create markdown components for chat message component export const createMarkdownComponents = ( renderCopyButton: (text: any) => JSX.Element, renderMarkdownElement: (props: any) => JSX.Element, askAboutCode: (code: any) => void ): Components => ({ - code: ({ node, className, children, ...props }: { - node?: import('hast').Element, - className?: string, - children?: React.ReactNode, - [key: string]: any, + code: ({ + node, + className, + children, + ...props + }: { + node?: import("hast").Element + className?: string + children?: React.ReactNode + [key: string]: any }) => { const match = /language-(\w+)/.exec(className || "") @@ -55,25 +60,30 @@ export const createMarkdownComponents = ( ) : ( - {children} + + {children} + ) }, - // Render markdown elements - p: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), - h1: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), - h2: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), - h3: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), - h4: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), - h5: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), - h6: ({ node, children, ...props }) => renderMarkdownElement({ node, children, ...props }), + // Render markdown elements + p: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), + h1: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), + h2: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), + h3: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), + h4: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), + h5: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), + h6: ({ node, children, ...props }) => + renderMarkdownElement({ node, children, ...props }), ul: (props) => ( -
    - {props.children} -
+
    {props.children}
), ol: (props) => ( -
    - {props.children} -
+
    {props.children}
), }) diff --git a/frontend/components/editor/AIChat/types/index.ts b/frontend/components/editor/AIChat/types/index.ts index 40978b8..bc69ac7 100644 --- a/frontend/components/editor/AIChat/types/index.ts +++ b/frontend/components/editor/AIChat/types/index.ts @@ -1,28 +1,28 @@ -import * as monaco from 'monaco-editor' import { TFile, TFolder } from "@/lib/types" -import { Socket } from 'socket.io-client'; +import * as monaco from "monaco-editor" +import { Socket } from "socket.io-client" // Allowed file types for context tabs export const ALLOWED_FILE_TYPES = { // Text files - 'text/plain': true, - 'text/markdown': true, - 'text/csv': true, + "text/plain": true, + "text/markdown": true, + "text/csv": true, // Code files - 'application/json': true, - 'text/javascript': true, - 'text/typescript': true, - 'text/html': true, - 'text/css': true, + "application/json": true, + "text/javascript": true, + "text/typescript": true, + "text/html": true, + "text/css": true, // Documents - 'application/pdf': true, + "application/pdf": true, // Images - 'image/jpeg': true, - 'image/png': true, - 'image/gif': true, - 'image/webp': true, - 'image/svg+xml': true, -} as const; + "image/jpeg": true, + "image/png": true, + "image/gif": true, + "image/webp": true, + "image/svg+xml": true, +} as const // Message interface export interface Message { @@ -45,8 +45,13 @@ export interface AIChatProps { activeFileContent: string activeFileName: string onClose: () => void - editorRef: React.MutableRefObject - lastCopiedRangeRef: React.MutableRefObject<{ startLine: number; endLine: number } | null> + editorRef: React.MutableRefObject< + monaco.editor.IStandaloneCodeEditor | undefined + > + lastCopiedRangeRef: React.MutableRefObject<{ + startLine: number + endLine: number + } | null> files: (TFile | TFolder)[] } @@ -58,11 +63,27 @@ export interface ChatInputProps { handleSend: (useFullContext?: boolean) => void handleStopGeneration: () => void onImageUpload: (file: File) => void - addContextTab: (type: string, title: string, content: string, lineRange?: { start: number, end: number }) => void + addContextTab: ( + type: string, + title: string, + content: string, + lineRange?: { start: number; end: number } + ) => void activeFileName?: string - editorRef: React.MutableRefObject - lastCopiedRangeRef: React.MutableRefObject<{ startLine: number; endLine: number } | null> - contextTabs: { id: string; type: string; title: string; content: string; lineRange?: { start: number; end: number } }[] + editorRef: React.MutableRefObject< + monaco.editor.IStandaloneCodeEditor | undefined + > + lastCopiedRangeRef: React.MutableRefObject<{ + startLine: number + endLine: number + } | null> + contextTabs: { + id: string + type: string + title: string + content: string + lineRange?: { start: number; end: number } + }[] onRemoveTab: (id: string) => void textareaRef: React.RefObject } @@ -74,7 +95,11 @@ export interface MessageProps { content: string context?: string } - setContext: (context: string | null, name: string, range?: { start: number, end: number }) => void + setContext: ( + context: string | null, + name: string, + range?: { start: number; end: number } + ) => void setIsContextExpanded: (isExpanded: boolean) => void socket: Socket | null } diff --git a/frontend/components/editor/generate.tsx b/frontend/components/editor/generate.tsx index 0b5ff39..6062e07 100644 --- a/frontend/components/editor/generate.tsx +++ b/frontend/components/editor/generate.tsx @@ -72,7 +72,7 @@ export default function GenerateInput({ fileName: data.fileName, code: data.code, line: data.line, - instructions: regenerate ? currentPrompt : input + instructions: regenerate ? currentPrompt : input, }, (res: { response: string; success: boolean }) => { console.log("Generated code", res.response, res.success) diff --git a/frontend/components/editor/index.tsx b/frontend/components/editor/index.tsx index a516114..bf7e5ec 100644 --- a/frontend/components/editor/index.tsx +++ b/frontend/components/editor/index.tsx @@ -173,7 +173,10 @@ export default function CodeEditor({ const previewWindowRef = useRef<{ refreshIframe: () => void }>(null) // Ref to store the last copied range in the editor to be used in the AIChat component - const lastCopiedRangeRef = useRef<{ startLine: number; endLine: number } | null>(null); + const lastCopiedRangeRef = useRef<{ + startLine: number + endLine: number + } | null>(null) const debouncedSetIsSelected = useRef( debounce((value: boolean) => { @@ -260,14 +263,14 @@ export default function CodeEditor({ // Store the last copied range in the editor to be used in the AIChat component editor.onDidChangeCursorSelection((e) => { - const selection = editor.getSelection(); + const selection = editor.getSelection() if (selection) { lastCopiedRangeRef.current = { startLine: selection.startLineNumber, - endLine: selection.endLineNumber - }; + endLine: selection.endLineNumber, + } } - }); + }) } // Call the function with your file structure @@ -658,7 +661,7 @@ export default function CodeEditor({ // Socket event listener effect useEffect(() => { - const onConnect = () => { } + const onConnect = () => {} const onDisconnect = () => { setTerminals([]) @@ -786,8 +789,8 @@ export default function CodeEditor({ ? numTabs === 1 ? null : index < numTabs - 1 - ? tabs[index + 1].id - : tabs[index - 1].id + ? tabs[index + 1].id + : tabs[index - 1].id : activeFileId setTabs((prev) => prev.filter((t) => t.id !== id)) @@ -853,9 +856,13 @@ export default function CodeEditor({ } const handleDeleteFile = (file: TFile) => { - socket?.emit("deleteFile", { fileId: file.id }, (response: (TFolder | TFile)[]) => { - setFiles(response) - }) + socket?.emit( + "deleteFile", + { fileId: file.id }, + (response: (TFolder | TFile)[]) => { + setFiles(response) + } + ) closeTab(file.id) } @@ -867,10 +874,14 @@ export default function CodeEditor({ closeTabs(response) ) - socket?.emit("deleteFolder", { folderId: folder.id }, (response: (TFolder | TFile)[]) => { - setFiles(response) - setDeletingFolderId("") - }) + socket?.emit( + "deleteFolder", + { folderId: folder.id }, + (response: (TFolder | TFile)[]) => { + setFiles(response) + setDeletingFolderId("") + } + ) } const togglePreviewPanel = () => { @@ -911,7 +922,7 @@ export default function CodeEditor({ { }} + setOpen={() => {}} /> @@ -953,8 +964,8 @@ export default function CodeEditor({ code: (isSelected && editorRef?.getSelection() ? editorRef - ?.getModel() - ?.getValueInRange(editorRef?.getSelection()!) + ?.getModel() + ?.getValueInRange(editorRef?.getSelection()!) : editorRef?.getValue()) ?? "", line: generate.line, }} @@ -1086,62 +1097,62 @@ export default function CodeEditor({ ) : // Note clerk.loaded is required here due to a bug: https://github.com/clerk/javascript/issues/1643 - clerk.loaded ? ( - <> - {provider && userInfo ? ( - - ) : null} - { - // If the new content is different from the cached content, update it - if (value !== fileContents[activeFileId]) { - setActiveFileContent(value ?? "") // Update the active file content - // Mark the file as unsaved by setting 'saved' to false - setTabs((prev) => - prev.map((tab) => - tab.id === activeFileId - ? { ...tab, saved: false } - : tab - ) + clerk.loaded ? ( + <> + {provider && userInfo ? ( + + ) : null} + { + // If the new content is different from the cached content, update it + if (value !== fileContents[activeFileId]) { + setActiveFileContent(value ?? "") // Update the active file content + // Mark the file as unsaved by setting 'saved' to false + setTabs((prev) => + prev.map((tab) => + tab.id === activeFileId + ? { ...tab, saved: false } + : tab ) - } else { - // If the content matches the cached content, mark the file as saved - setTabs((prev) => - prev.map((tab) => - tab.id === activeFileId - ? { ...tab, saved: true } - : tab - ) + ) + } else { + // If the content matches the cached content, mark the file as saved + setTabs((prev) => + prev.map((tab) => + tab.id === activeFileId + ? { ...tab, saved: true } + : tab ) - } - }} - options={{ - tabSize: 2, - minimap: { - enabled: false, - }, - padding: { - bottom: 4, - top: 4, - }, - scrollBeyondLastLine: false, - fixedOverflowWidgets: true, - fontFamily: "var(--font-geist-mono)", - }} - theme={theme === "light" ? "vs" : "vs-dark"} - value={activeFileContent} - /> - - ) : ( -
- - Waiting for Clerk to load... -
- )} + ) + } + }} + options={{ + tabSize: 2, + minimap: { + enabled: false, + }, + padding: { + bottom: 4, + top: 4, + }, + scrollBeyondLastLine: false, + fixedOverflowWidgets: true, + fontFamily: "var(--font-geist-mono)", + }} + theme={theme === "light" ? "vs" : "vs-dark"} + value={activeFileContent} + /> + + ) : ( +
+ + Waiting for Clerk to load... +
+ )} @@ -1151,10 +1162,10 @@ export default function CodeEditor({ isAIChatOpen && isHorizontalLayout ? "horizontal" : isAIChatOpen - ? "vertical" - : isHorizontalLayout - ? "horizontal" - : "vertical" + ? "vertical" + : isHorizontalLayout + ? "horizontal" + : "vertical" } > Share - + + ) : null} diff --git a/frontend/components/editor/navbar/share.tsx b/frontend/components/editor/navbar/share.tsx index 88a2854..748c78d 100644 --- a/frontend/components/editor/navbar/share.tsx +++ b/frontend/components/editor/navbar/share.tsx @@ -143,11 +143,7 @@ export default function ShareSandboxModal({
{shared.map((user) => ( - + ))}
diff --git a/frontend/components/editor/sidebar/index.tsx b/frontend/components/editor/sidebar/index.tsx index c68ec48..0af1c99 100644 --- a/frontend/components/editor/sidebar/index.tsx +++ b/frontend/components/editor/sidebar/index.tsx @@ -93,7 +93,7 @@ export default function Sidebar({ "moveFile", { fileId, - folderId + folderId, }, (response: (TFolder | TFile)[]) => { setFiles(response) @@ -203,20 +203,18 @@ export default function Sidebar({ variant="ghost" className={cn( "w-full justify-start text-sm font-normal h-8 px-2 mb-2 border-t", - isAIChatOpen - ? "bg-muted-foreground/25 text-foreground" + isAIChatOpen + ? "bg-muted-foreground/25 text-foreground" : "text-muted-foreground" )} onClick={toggleAIChat} aria-disabled={false} style={{ opacity: 1 }} > - AI Chat diff --git a/frontend/components/editor/theme.json b/frontend/components/editor/theme.json index a0a8f03..d15b024 100644 --- a/frontend/components/editor/theme.json +++ b/frontend/components/editor/theme.json @@ -694,4 +694,4 @@ } ], "encodedTokensColors": [] -} \ No newline at end of file +} diff --git a/frontend/components/ui/alert.tsx b/frontend/components/ui/alert.tsx index 5afd41d..2674ae7 100644 --- a/frontend/components/ui/alert.tsx +++ b/frontend/components/ui/alert.tsx @@ -1,5 +1,5 @@ -import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" +import * as React from "react" import { cn } from "@/lib/utils" @@ -56,4 +56,4 @@ const AlertDescription = React.forwardRef< )) AlertDescription.displayName = "AlertDescription" -export { Alert, AlertTitle, AlertDescription } +export { Alert, AlertDescription, AlertTitle } diff --git a/frontend/components/ui/progress.tsx b/frontend/components/ui/progress.tsx index 4fc3b47..efbac30 100644 --- a/frontend/components/ui/progress.tsx +++ b/frontend/components/ui/progress.tsx @@ -1,7 +1,7 @@ "use client" -import * as React from "react" import * as ProgressPrimitive from "@radix-ui/react-progress" +import * as React from "react" import { cn } from "@/lib/utils" diff --git a/frontend/components/ui/theme-provider.tsx b/frontend/components/ui/theme-provider.tsx index 5135dd0..19dfaa9 100644 --- a/frontend/components/ui/theme-provider.tsx +++ b/frontend/components/ui/theme-provider.tsx @@ -6,4 +6,3 @@ import { type ThemeProviderProps } from "next-themes/dist/types" export function ThemeProvider({ children, ...props }: ThemeProviderProps) { return {children} } - \ No newline at end of file diff --git a/frontend/components/ui/tooltip.tsx b/frontend/components/ui/tooltip.tsx index a66b3f2..e6cde1b 100644 --- a/frontend/components/ui/tooltip.tsx +++ b/frontend/components/ui/tooltip.tsx @@ -1,7 +1,7 @@ "use client" -import * as React from "react" import * as TooltipPrimitive from "@radix-ui/react-tooltip" +import * as React from "react" import { cn } from "@/lib/utils" @@ -29,4 +29,4 @@ const TooltipContent = React.forwardRef< )) TooltipContent.displayName = TooltipPrimitive.Content.displayName -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } diff --git a/frontend/context/TerminalContext.tsx b/frontend/context/TerminalContext.tsx index 5af74a8..1dd00c2 100644 --- a/frontend/context/TerminalContext.tsx +++ b/frontend/context/TerminalContext.tsx @@ -63,7 +63,7 @@ export const TerminalProvider: React.FC<{ children: React.ReactNode }> = ({ terminals, setTerminals, setActiveTerminalId, - setClosingTerminal: () => { }, + setClosingTerminal: () => {}, socket, activeTerminalId, }) diff --git a/frontend/lib/file-extension-to-language.json b/frontend/lib/file-extension-to-language.json index 7aff12b..ed584a5 100644 --- a/frontend/lib/file-extension-to-language.json +++ b/frontend/lib/file-extension-to-language.json @@ -38,7 +38,7 @@ "csl": "xml", "cson": "coffeescript", "csproj": "xml", - "css":"css", + "css": "css", "ct": "xml", "ctp": "php", "cxx": "cpp", diff --git a/frontend/lib/terminal.ts b/frontend/lib/terminal.ts index 1d0edbc..253f3c2 100644 --- a/frontend/lib/terminal.ts +++ b/frontend/lib/terminal.ts @@ -83,8 +83,8 @@ export const closeTerminal = ({ ? numTerminals === 1 ? null : index < numTerminals - 1 - ? terminals[index + 1].id - : terminals[index - 1].id + ? terminals[index + 1].id + : terminals[index - 1].id : activeTerminalId setTerminals((prev) => prev.filter((t) => t.id !== term.id)) diff --git a/frontend/lib/tsconfig.ts b/frontend/lib/tsconfig.ts index f1d35ce..5986d42 100644 --- a/frontend/lib/tsconfig.ts +++ b/frontend/lib/tsconfig.ts @@ -74,10 +74,10 @@ function mapModule(module: string): monaco.languages.typescript.ModuleKind { } function mapJSX(jsx: string | undefined): monaco.languages.typescript.JsxEmit { - if (!jsx || typeof jsx !== 'string') { + if (!jsx || typeof jsx !== "string") { return monaco.languages.typescript.JsxEmit.React // Default value } - + const jsxMap: { [key: string]: monaco.languages.typescript.JsxEmit } = { preserve: monaco.languages.typescript.JsxEmit.Preserve, react: monaco.languages.typescript.JsxEmit.React, diff --git a/frontend/lib/username-generator.ts b/frontend/lib/username-generator.ts index f2d4192..a625ece 100644 --- a/frontend/lib/username-generator.ts +++ b/frontend/lib/username-generator.ts @@ -1,82 +1,181 @@ // Constants for username generation const WORDS = { - adjectives: [ - "azure", "crimson", "golden", "silver", "violet", "emerald", "cobalt", "amber", "coral", "jade", - "cyber", "digital", "quantum", "neural", "binary", "cosmic", "stellar", "atomic", "crypto", "nano", - "swift", "brave", "clever", "wise", "noble", "rapid", "bright", "sharp", "keen", "bold", - "dynamic", "epic", "mega", "ultra", "hyper", "super", "prime", "elite", "alpha", "omega", - "pixel", "vector", "sonic", "laser", "matrix", "nexus", "proxy", "cloud", "data", "tech", - ], - nouns: [ - "coder", "hacker", "dev", "ninja", "guru", "wizard", "admin", "mod", "chief", "boss", - "wolf", "eagle", "phoenix", "dragon", "tiger", "falcon", "shark", "lion", "hawk", "bear", - "byte", "bit", "node", "stack", "cache", "chip", "core", "net", "web", "app", - "star", "nova", "pulsar", "comet", "nebula", "quasar", "cosmos", "orbit", "astro", "solar", - "mind", "soul", "spark", "pulse", "force", "power", "wave", "storm", "flash", "surge", - ], - prefixes: [ - "the", "mr", "ms", "dr", "pro", "master", "lord", "captain", "chief", "agent", - ], - } as const; - - // Helper function to get random element from array - const getRandomElement = (array: readonly T[]): T => { - return array[Math.floor(Math.random() * array.length)]; - }; - - // Username pattern generators - const usernamePatterns = { - basic: (): string => { - const adjective = getRandomElement(WORDS.adjectives); - const noun = getRandomElement(WORDS.nouns); - const number = Math.floor(Math.random() * 10000); - return `${adjective}${noun}${number}`; - }, - - prefixed: (): string => { - const prefix = getRandomElement(WORDS.prefixes); - const noun = getRandomElement(WORDS.nouns); - const number = Math.floor(Math.random() * 100); - return `${prefix}${noun}${number}`; - }, - - doubleAdjective: (): string => { - const adj1 = getRandomElement(WORDS.adjectives); - const adj2 = getRandomElement(WORDS.adjectives); - const noun = getRandomElement(WORDS.nouns); - return `${adj1}${adj2}${noun}`; - }, - - doubleNoun: (): string => { - const noun1 = getRandomElement(WORDS.nouns); - const noun2 = getRandomElement(WORDS.nouns); - const number = Math.floor(Math.random() * 100); - return `${noun1}${number}${noun2}`; - }, - }; - - export function generateUsername(): string { - const patterns = Object.values(usernamePatterns); - const selectedPattern = getRandomElement(patterns); - return selectedPattern(); + adjectives: [ + "azure", + "crimson", + "golden", + "silver", + "violet", + "emerald", + "cobalt", + "amber", + "coral", + "jade", + "cyber", + "digital", + "quantum", + "neural", + "binary", + "cosmic", + "stellar", + "atomic", + "crypto", + "nano", + "swift", + "brave", + "clever", + "wise", + "noble", + "rapid", + "bright", + "sharp", + "keen", + "bold", + "dynamic", + "epic", + "mega", + "ultra", + "hyper", + "super", + "prime", + "elite", + "alpha", + "omega", + "pixel", + "vector", + "sonic", + "laser", + "matrix", + "nexus", + "proxy", + "cloud", + "data", + "tech", + ], + nouns: [ + "coder", + "hacker", + "dev", + "ninja", + "guru", + "wizard", + "admin", + "mod", + "chief", + "boss", + "wolf", + "eagle", + "phoenix", + "dragon", + "tiger", + "falcon", + "shark", + "lion", + "hawk", + "bear", + "byte", + "bit", + "node", + "stack", + "cache", + "chip", + "core", + "net", + "web", + "app", + "star", + "nova", + "pulsar", + "comet", + "nebula", + "quasar", + "cosmos", + "orbit", + "astro", + "solar", + "mind", + "soul", + "spark", + "pulse", + "force", + "power", + "wave", + "storm", + "flash", + "surge", + ], + prefixes: [ + "the", + "mr", + "ms", + "dr", + "pro", + "master", + "lord", + "captain", + "chief", + "agent", + ], +} as const + +// Helper function to get random element from array +const getRandomElement = (array: readonly T[]): T => { + return array[Math.floor(Math.random() * array.length)] +} + +// Username pattern generators +const usernamePatterns = { + basic: (): string => { + const adjective = getRandomElement(WORDS.adjectives) + const noun = getRandomElement(WORDS.nouns) + const number = Math.floor(Math.random() * 10000) + return `${adjective}${noun}${number}` + }, + + prefixed: (): string => { + const prefix = getRandomElement(WORDS.prefixes) + const noun = getRandomElement(WORDS.nouns) + const number = Math.floor(Math.random() * 100) + return `${prefix}${noun}${number}` + }, + + doubleAdjective: (): string => { + const adj1 = getRandomElement(WORDS.adjectives) + const adj2 = getRandomElement(WORDS.adjectives) + const noun = getRandomElement(WORDS.nouns) + return `${adj1}${adj2}${noun}` + }, + + doubleNoun: (): string => { + const noun1 = getRandomElement(WORDS.nouns) + const noun2 = getRandomElement(WORDS.nouns) + const number = Math.floor(Math.random() * 100) + return `${noun1}${number}${noun2}` + }, +} + +export function generateUsername(): string { + const patterns = Object.values(usernamePatterns) + const selectedPattern = getRandomElement(patterns) + return selectedPattern() +} + +export async function generateUniqueUsername( + checkExists: (username: string) => Promise +): Promise { + const MAX_ATTEMPTS = 10 + let attempts = 0 + let username = generateUsername() + + while ((await checkExists(username)) && attempts < MAX_ATTEMPTS) { + username = generateUsername() + attempts++ } - - export async function generateUniqueUsername( - checkExists: (username: string) => Promise - ): Promise { - const MAX_ATTEMPTS = 10; - let attempts = 0; - let username = generateUsername(); - - while (await checkExists(username) && attempts < MAX_ATTEMPTS) { - username = generateUsername(); - attempts++; - } - - if (attempts >= MAX_ATTEMPTS) { - // Add a large random number to ensure uniqueness - username = generateUsername() + Math.floor(Math.random() * 1000000); - } - - return username; - } \ No newline at end of file + + if (attempts >= MAX_ATTEMPTS) { + // Add a large random number to ensure uniqueness + username = generateUsername() + Math.floor(Math.random() * 1000000) + } + + return username +} diff --git a/frontend/liveblocks.config.ts b/frontend/liveblocks.config.ts index df22c32..e71427d 100644 --- a/frontend/liveblocks.config.ts +++ b/frontend/liveblocks.config.ts @@ -1,5 +1,5 @@ import { createClient } from "@liveblocks/client" -import { createRoomContext, createLiveblocksContext } from "@liveblocks/react" +import { createLiveblocksContext, createRoomContext } from "@liveblocks/react" import YLiveblocksProvider from "@liveblocks/yjs" import { colors } from "./lib/colors" diff --git a/frontend/react-syntax-highlighter.d.ts b/frontend/react-syntax-highlighter.d.ts index 5415aa9..4e92fd6 100644 --- a/frontend/react-syntax-highlighter.d.ts +++ b/frontend/react-syntax-highlighter.d.ts @@ -1,3 +1,2 @@ -declare module 'react-syntax-highlighter'; -declare module 'react-syntax-highlighter/dist/esm/styles/prism'; - +declare module "react-syntax-highlighter" +declare module "react-syntax-highlighter/dist/esm/styles/prism"