Merge branch 'main' into feat/profile-page
This commit is contained in:
@ -16,7 +16,7 @@ export const projectTemplates: {
|
||||
id: "vanillajs",
|
||||
name: "HTML/JS",
|
||||
icon: "/project-icons/more.svg",
|
||||
description: "More coming soon, feel free to contribute on GitHub",
|
||||
description: "A simple HTML/JS project for building web apps",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
@ -33,4 +33,11 @@ export const projectTemplates: {
|
||||
description: "A faster way to build and share data apps",
|
||||
disabled: false,
|
||||
},
|
||||
{
|
||||
id: "php",
|
||||
name: "PHP",
|
||||
description: "PHP development environment",
|
||||
icon: "/project-icons/php.svg",
|
||||
disabled: false
|
||||
},
|
||||
]
|
||||
|
@ -38,7 +38,7 @@
|
||||
"csl": "xml",
|
||||
"cson": "coffeescript",
|
||||
"csproj": "xml",
|
||||
"css":"css",
|
||||
"css": "css",
|
||||
"ct": "xml",
|
||||
"ctp": "php",
|
||||
"cxx": "cpp",
|
||||
|
248
frontend/lib/templates/index.ts
Normal file
248
frontend/lib/templates/index.ts
Normal file
@ -0,0 +1,248 @@
|
||||
export interface TemplateConfig {
|
||||
id: string
|
||||
name: string,
|
||||
runCommand: string,
|
||||
fileStructure: {
|
||||
[key: string]: {
|
||||
purpose: string
|
||||
description: string
|
||||
}
|
||||
}
|
||||
conventions: string[]
|
||||
dependencies?: {
|
||||
[key: string]: string
|
||||
}
|
||||
scripts?: {
|
||||
[key: string]: string
|
||||
}
|
||||
}
|
||||
|
||||
export const templateConfigs: { [key: string]: TemplateConfig } = {
|
||||
reactjs: {
|
||||
id: "reactjs",
|
||||
name: "React",
|
||||
runCommand: "npm run dev",
|
||||
fileStructure: {
|
||||
"src/": {
|
||||
purpose: "source",
|
||||
description: "Contains all React components and application logic"
|
||||
},
|
||||
"src/components/": {
|
||||
purpose: "components",
|
||||
description: "Reusable React components"
|
||||
},
|
||||
"src/lib/": {
|
||||
purpose: "utilities",
|
||||
description: "Utility functions and shared code"
|
||||
},
|
||||
"src/App.tsx": {
|
||||
purpose: "entry",
|
||||
description: "Main application component"
|
||||
},
|
||||
"src/index.tsx": {
|
||||
purpose: "entry",
|
||||
description: "Application entry point"
|
||||
},
|
||||
"src/index.css": {
|
||||
purpose: "styles",
|
||||
description: "Global CSS styles"
|
||||
},
|
||||
"public/": {
|
||||
purpose: "static",
|
||||
description: "Static assets and index.html"
|
||||
},
|
||||
"tsconfig.json": {
|
||||
purpose: "config",
|
||||
description: "TypeScript configuration"
|
||||
},
|
||||
"vite.config.ts": {
|
||||
purpose: "config",
|
||||
description: "Vite bundler configuration"
|
||||
},
|
||||
"package.json": {
|
||||
purpose: "config",
|
||||
description: "Project dependencies and scripts"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use functional components with hooks",
|
||||
"Follow React naming conventions (PascalCase for components)",
|
||||
"Keep components small and focused",
|
||||
"Use TypeScript for type safety"
|
||||
],
|
||||
dependencies: {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.441.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"tailwind-merge": "^2.5.2",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
scripts: {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"preview": "vite preview",
|
||||
}
|
||||
},
|
||||
// Next.js template config
|
||||
nextjs: {
|
||||
id: "nextjs",
|
||||
name: "NextJS",
|
||||
runCommand: "npm run dev",
|
||||
fileStructure: {
|
||||
"pages/": {
|
||||
purpose: "routing",
|
||||
description: "Page components and API routes"
|
||||
},
|
||||
"pages/api/": {
|
||||
purpose: "api",
|
||||
description: "API route handlers"
|
||||
},
|
||||
"pages/_app.tsx": {
|
||||
purpose: "entry",
|
||||
description: "Application wrapper component"
|
||||
},
|
||||
"pages/index.tsx": {
|
||||
purpose: "page",
|
||||
description: "Homepage component"
|
||||
},
|
||||
"public/": {
|
||||
purpose: "static",
|
||||
description: "Static assets and files"
|
||||
},
|
||||
"styles/": {
|
||||
purpose: "styles",
|
||||
description: "CSS modules and global styles"
|
||||
},
|
||||
"styles/globals.css": {
|
||||
purpose: "styles",
|
||||
description: "Global CSS styles"
|
||||
},
|
||||
"styles/Home.module.css": {
|
||||
purpose: "styles",
|
||||
description: "Homepage-specific styles"
|
||||
},
|
||||
"next.config.js": {
|
||||
purpose: "config",
|
||||
description: "Next.js configuration"
|
||||
},
|
||||
"next-env.d.ts": {
|
||||
purpose: "types",
|
||||
description: "Next.js TypeScript declarations"
|
||||
},
|
||||
"tsconfig.json": {
|
||||
purpose: "config",
|
||||
description: "TypeScript configuration"
|
||||
},
|
||||
"package.json": {
|
||||
purpose: "config",
|
||||
description: "Project dependencies and scripts"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use file-system based routing",
|
||||
"Keep API routes in pages/api",
|
||||
"Use CSS Modules for component styles",
|
||||
"Follow Next.js data fetching patterns"
|
||||
],
|
||||
dependencies: {
|
||||
"next": "^14.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"tailwindcss": "^3.4.1"
|
||||
},
|
||||
scripts: {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
}
|
||||
},
|
||||
// Streamlit template config
|
||||
streamlit: {
|
||||
id: "streamlit",
|
||||
name: "Streamlit",
|
||||
runCommand: "./venv/bin/streamlit run main.py --server.runOnSave true",
|
||||
fileStructure: {
|
||||
"main.py": {
|
||||
purpose: "entry",
|
||||
description: "Main Streamlit application file"
|
||||
},
|
||||
"requirements.txt": {
|
||||
purpose: "dependencies",
|
||||
description: "Python package dependencies"
|
||||
},
|
||||
"Procfile": {
|
||||
purpose: "deployment",
|
||||
description: "Deployment configuration for hosting platforms"
|
||||
},
|
||||
"venv/": {
|
||||
purpose: "environment",
|
||||
description: "Python virtual environment directory"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use Streamlit components for UI",
|
||||
"Follow PEP 8 style guide",
|
||||
"Keep dependencies in requirements.txt",
|
||||
"Use virtual environment for isolation"
|
||||
],
|
||||
dependencies: {
|
||||
"streamlit": "^1.40.0",
|
||||
"altair": "^5.5.0"
|
||||
},
|
||||
scripts: {
|
||||
"start": "streamlit run main.py",
|
||||
"dev": "./venv/bin/streamlit run main.py --server.runOnSave true"
|
||||
}
|
||||
},
|
||||
// HTML template config
|
||||
vanillajs: {
|
||||
id: "vanillajs",
|
||||
name: "HTML/JS",
|
||||
runCommand: "npm run dev",
|
||||
fileStructure: {
|
||||
"index.html": {
|
||||
purpose: "entry",
|
||||
description: "Main HTML entry point"
|
||||
},
|
||||
"style.css": {
|
||||
purpose: "styles",
|
||||
description: "Global CSS styles"
|
||||
},
|
||||
"script.js": {
|
||||
purpose: "scripts",
|
||||
description: "JavaScript application logic"
|
||||
},
|
||||
"package.json": {
|
||||
purpose: "config",
|
||||
description: "Project dependencies and scripts"
|
||||
},
|
||||
"package-lock.json": {
|
||||
purpose: "config",
|
||||
description: "Locked dependency versions"
|
||||
},
|
||||
"vite.config.js": {
|
||||
purpose: "config",
|
||||
description: "Vite bundler configuration"
|
||||
}
|
||||
},
|
||||
conventions: [
|
||||
"Use semantic HTML elements",
|
||||
"Keep CSS modular and organized",
|
||||
"Write clean, modular JavaScript",
|
||||
"Follow modern ES6+ practices"
|
||||
],
|
||||
dependencies: {
|
||||
"vite": "^5.0.12"
|
||||
},
|
||||
scripts: {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
}
|
||||
}
|
||||
}
|
@ -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))
|
||||
|
19
frontend/lib/tiers.ts
Normal file
19
frontend/lib/tiers.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export const TIERS = {
|
||||
FREE: {
|
||||
// generations: 100,
|
||||
// maxTokens: 1024,
|
||||
generations: 1000,
|
||||
maxTokens: 4096,
|
||||
model: "claude-3-5-sonnet-20240620",
|
||||
},
|
||||
PRO: {
|
||||
generations: 500,
|
||||
maxTokens: 2048,
|
||||
model: "claude-3-5-sonnet-20240620",
|
||||
},
|
||||
ENTERPRISE: {
|
||||
generations: 1000,
|
||||
maxTokens: 4096,
|
||||
model: "claude-3-5-sonnet-20240620",
|
||||
},
|
||||
}
|
@ -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,
|
||||
|
@ -10,6 +10,9 @@ export type User = {
|
||||
generations: number
|
||||
sandbox: Sandbox[]
|
||||
usersToSandboxes: UsersToSandboxes[]
|
||||
tier: "FREE" | "PRO" | "ENTERPRISE"
|
||||
tierExpiresAt: Date
|
||||
lastResetDate?: number
|
||||
}
|
||||
|
||||
export type Sandbox = {
|
||||
|
@ -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 = <T>(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 = <T>(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<boolean>
|
||||
): Promise<string> {
|
||||
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<boolean>
|
||||
): Promise<string> {
|
||||
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;
|
||||
}
|
||||
|
||||
if (attempts >= MAX_ATTEMPTS) {
|
||||
// Add a large random number to ensure uniqueness
|
||||
username = generateUsername() + Math.floor(Math.random() * 1000000)
|
||||
}
|
||||
|
||||
return username
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { type ClassValue, clsx } from "clsx"
|
||||
// import { toast } from "sonner"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
import fileExtToLang from "./file-extension-to-language.json"
|
||||
import { Sandbox, TFile, TFolder } from "./types"
|
||||
import { TFile, TFolder } from "./types"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
@ -38,31 +38,6 @@ export function validateName(
|
||||
return { status: true, message: "" }
|
||||
}
|
||||
|
||||
export function addNew(
|
||||
name: string,
|
||||
type: "file" | "folder",
|
||||
setFiles: React.Dispatch<React.SetStateAction<(TFolder | TFile)[]>>,
|
||||
sandboxData: Sandbox
|
||||
) {
|
||||
if (type === "file") {
|
||||
setFiles((prev) => [
|
||||
...prev,
|
||||
{ id: `projects/${sandboxData.id}/${name}`, name, type: "file" },
|
||||
])
|
||||
} else {
|
||||
console.log("adding folder")
|
||||
setFiles((prev) => [
|
||||
...prev,
|
||||
{
|
||||
id: `projects/${sandboxData.id}/${name}`,
|
||||
name,
|
||||
type: "folder",
|
||||
children: [],
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
export function debounce<T extends (...args: any[]) => void>(
|
||||
func: T,
|
||||
wait: number
|
||||
|
Reference in New Issue
Block a user