Leon4gr45 commited on
Commit
51ffe19
·
verified ·
1 Parent(s): 4f34fc3

Upload folder using huggingface_hub (part 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. package-lock.json +0 -0
  2. package.json +101 -0
  3. packages/create-open-lovable/index.js +94 -0
  4. packages/create-open-lovable/lib/installer.js +261 -0
  5. packages/create-open-lovable/lib/prompts.js +190 -0
  6. packages/create-open-lovable/package.json +36 -0
  7. packages/create-open-lovable/templates/e2b/.env.example +25 -0
  8. packages/create-open-lovable/templates/e2b/README.md +38 -0
  9. packages/create-open-lovable/templates/vercel/.env.example +28 -0
  10. packages/create-open-lovable/templates/vercel/README.md +52 -0
  11. pnpm-lock.yaml +0 -0
  12. postcss.config.mjs +8 -0
  13. public/compressor.json +16 -0
  14. public/file.svg +1 -0
  15. public/firecrawl-logo +0 -0
  16. public/firecrawl.svg +4 -0
  17. public/globe.svg +1 -0
  18. public/window.svg +1 -0
  19. requirements.txt +4 -0
  20. scripts/run.sh +50 -0
  21. scripts/run_latex.sh +44 -0
  22. scripts/run_latex_llm.sh +45 -0
  23. scripts/run_llm.sh +51 -0
  24. styles/additional-styles/custom-fonts.css +0 -0
  25. styles/additional-styles/theme.css +58 -0
  26. styles/additional-styles/utility-patterns.css +112 -0
  27. styles/chrome-bug.css +12 -0
  28. styles/colors.json +182 -0
  29. styles/components/.cursor/rules/component-styles.md +32 -0
  30. styles/components/button.css +72 -0
  31. styles/components/code.css +44 -0
  32. styles/components/index.css +10 -0
  33. styles/design-system/.cursor/rules/design-system.md +81 -0
  34. styles/design-system/animations.css +92 -0
  35. styles/design-system/base/body.css +132 -0
  36. styles/design-system/base/layout.css +212 -0
  37. styles/design-system/base/reset.css +132 -0
  38. styles/design-system/colors.css +135 -0
  39. styles/design-system/fonts.css +8 -0
  40. styles/design-system/typography.css +243 -0
  41. styles/design-system/utilities.css +235 -0
  42. styles/fire.css +103 -0
  43. styles/inside-border-fix.css +47 -0
  44. styles/main.css +282 -0
  45. tailwind.config.ts +406 -0
  46. tsconfig.json +27 -0
  47. types/conversation.ts +50 -0
  48. types/file-manifest.ts +77 -0
  49. types/sandbox.ts +31 -0
  50. utils/cn.ts +5 -0
package-lock.json ADDED
The diff for this file is too large to render. See raw diff
 
package.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "open-lovable",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "next dev --turbopack",
8
+ "build": "next build",
9
+ "start": "next start",
10
+ "lint": "next lint",
11
+ "test:api": "node tests/api-endpoints.test.js",
12
+ "test:code": "node tests/code-execution.test.js",
13
+ "test:all": "npm run test:integration && npm run test:api && npm run test:code"
14
+ },
15
+ "dependencies": {
16
+ "@ai-sdk/anthropic": "^2.0.1",
17
+ "@ai-sdk/google": "^2.0.4",
18
+ "@ai-sdk/groq": "^2.0.0",
19
+ "@ai-sdk/openai": "^2.0.4",
20
+ "@anthropic-ai/sdk": "^0.57.0",
21
+ "@e2b/code-interpreter": "^2.0.0",
22
+ "@mendable/firecrawl-js": "^4.3.3",
23
+ "@radix-ui/react-accordion": "^1.2.12",
24
+ "@radix-ui/react-alert-dialog": "^1.1.15",
25
+ "@radix-ui/react-aspect-ratio": "^1.1.7",
26
+ "@radix-ui/react-avatar": "^1.1.10",
27
+ "@radix-ui/react-checkbox": "^1.3.3",
28
+ "@radix-ui/react-collapsible": "^1.1.12",
29
+ "@radix-ui/react-context-menu": "^2.2.16",
30
+ "@radix-ui/react-dialog": "^1.1.15",
31
+ "@radix-ui/react-dropdown-menu": "^2.1.16",
32
+ "@radix-ui/react-hover-card": "^1.1.15",
33
+ "@radix-ui/react-label": "^2.1.7",
34
+ "@radix-ui/react-menubar": "^1.1.16",
35
+ "@radix-ui/react-navigation-menu": "^1.2.14",
36
+ "@radix-ui/react-popover": "^1.1.15",
37
+ "@radix-ui/react-progress": "^1.1.7",
38
+ "@radix-ui/react-radio-group": "^1.3.8",
39
+ "@radix-ui/react-scroll-area": "^1.2.10",
40
+ "@radix-ui/react-select": "^2.2.6",
41
+ "@radix-ui/react-separator": "^1.1.7",
42
+ "@radix-ui/react-slider": "^1.3.6",
43
+ "@radix-ui/react-slot": "^1.2.3",
44
+ "@radix-ui/react-switch": "^1.2.5",
45
+ "@radix-ui/react-tabs": "^1.1.13",
46
+ "@radix-ui/react-toast": "^1.2.15",
47
+ "@radix-ui/react-toggle": "^1.1.10",
48
+ "@radix-ui/react-toggle-group": "^1.1.11",
49
+ "@radix-ui/react-tooltip": "^1.2.8",
50
+ "@tabler/icons-react": "^3.34.1",
51
+ "@tailwindcss/typography": "^0.5.16",
52
+ "@types/react-syntax-highlighter": "^15.5.13",
53
+ "@vercel/sandbox": "^0.0.17",
54
+ "ai": "^5.0.0",
55
+ "autoprefixer": "^10.4.21",
56
+ "axios": "^1.13.5",
57
+ "class-variance-authority": "^0.7.1",
58
+ "classnames": "^2.5.1",
59
+ "clsx": "^2.1.1",
60
+ "copy-to-clipboard": "^3.3.3",
61
+ "cors": "^2.8.5",
62
+ "dotenv": "^17.2.1",
63
+ "fast-xml-parser": "^5.3.6",
64
+ "framer-motion": "^12.23.12",
65
+ "groq-sdk": "^0.29.0",
66
+ "jotai": "^2.14.0",
67
+ "lodash-es": "^4.17.21",
68
+ "lucide-react": "^0.532.0",
69
+ "motion": "^12.23.12",
70
+ "nanoid": "^5.1.5",
71
+ "next": "15.4.3",
72
+ "next-themes": "^0.4.6",
73
+ "octokit": "^5.0.5",
74
+ "pixi.js": "^8.13.1",
75
+ "react": "19.1.0",
76
+ "react-dom": "19.1.0",
77
+ "react-hook-form": "^7.62.0",
78
+ "react-icons": "^5.5.0",
79
+ "react-syntax-highlighter": "^15.6.1",
80
+ "sonner": "^2.0.7",
81
+ "tailwind-gradient-mask-image": "^1.2.0",
82
+ "tailwind-merge": "^3.3.1",
83
+ "tailwindcss-animate": "^1.0.7",
84
+ "usehooks-ts": "^3.1.1",
85
+ "zod": "^3.25.76"
86
+ },
87
+ "devDependencies": {
88
+ "@eslint/eslintrc": "^3",
89
+ "@types/lodash-es": "^4.17.12",
90
+ "@types/node": "^20",
91
+ "@types/react": "^19",
92
+ "@types/react-dom": "^19",
93
+ "eslint": "^9",
94
+ "eslint-config-next": "15.4.3",
95
+ "postcss": "^8.5.6",
96
+ "postcss-import": "^16.1.1",
97
+ "postcss-nesting": "^13.0.2",
98
+ "tailwindcss": "^3.4.17",
99
+ "typescript": "^5"
100
+ }
101
+ }
packages/create-open-lovable/index.js ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import inquirer from 'inquirer';
5
+ import chalk from 'chalk';
6
+ import ora from 'ora';
7
+ import path from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { installer } from './lib/installer.js';
10
+ import { getPrompts } from './lib/prompts.js';
11
+
12
+ const __filename = fileURLToPath(import.meta.url);
13
+ const __dirname = path.dirname(__filename);
14
+
15
+ const program = new Command();
16
+
17
+ program
18
+ .name('create-open-lovable')
19
+ .description('Create a new Open Lovable project with your choice of sandbox provider')
20
+ .version('1.0.0')
21
+ .option('-s, --sandbox <provider>', 'Sandbox provider (e2b or vercel)')
22
+ .option('-n, --name <name>', 'Project name')
23
+ .option('-p, --path <path>', 'Installation path (defaults to current directory)')
24
+ .option('--skip-install', 'Skip npm install')
25
+ .option('--dry-run', 'Run without making changes')
26
+ .parse(process.argv);
27
+
28
+ const options = program.opts();
29
+
30
+ async function main() {
31
+ console.log(chalk.cyan('\n🚀 Welcome to Open Lovable Setup!\n'));
32
+
33
+ let config = {
34
+ sandbox: options.sandbox,
35
+ name: options.name || 'my-open-lovable',
36
+ path: options.path || process.cwd(),
37
+ skipInstall: options.skipInstall || false,
38
+ dryRun: options.dryRun || false
39
+ };
40
+
41
+ // Interactive mode if sandbox not specified
42
+ if (!config.sandbox) {
43
+ const prompts = getPrompts(config);
44
+ const answers = await inquirer.prompt(prompts);
45
+ config = { ...config, ...answers };
46
+ }
47
+
48
+ // Validate sandbox provider
49
+ if (!['e2b', 'vercel'].includes(config.sandbox)) {
50
+ console.error(chalk.red(`\n❌ Invalid sandbox provider: ${config.sandbox}`));
51
+ console.log(chalk.yellow('Valid options: e2b, vercel\n'));
52
+ process.exit(1);
53
+ }
54
+
55
+ console.log(chalk.gray('\nConfiguration:'));
56
+ console.log(chalk.gray(` Project: ${config.name}`));
57
+ console.log(chalk.gray(` Sandbox: ${config.sandbox}`));
58
+ console.log(chalk.gray(` Path: ${path.resolve(config.path, config.name)}\n`));
59
+
60
+ if (config.dryRun) {
61
+ console.log(chalk.yellow('🔍 Dry run mode - no files will be created\n'));
62
+ }
63
+
64
+ const spinner = ora('Creating project...').start();
65
+
66
+ try {
67
+ await installer({
68
+ ...config,
69
+ templatesDir: path.join(__dirname, 'templates')
70
+ });
71
+
72
+ spinner.succeed('Project created successfully!');
73
+
74
+ console.log(chalk.green('\n✅ Setup complete!\n'));
75
+ console.log(chalk.white('Next steps:'));
76
+ console.log(chalk.gray(` 1. cd ${config.name}`));
77
+ console.log(chalk.gray(` 2. Copy .env.example to .env and add your API keys`));
78
+ console.log(chalk.gray(` 3. npm run dev`));
79
+ console.log(chalk.gray('\nHappy coding! 🎉\n'));
80
+
81
+ } catch (error) {
82
+ spinner.fail('Setup failed');
83
+ console.error(chalk.red('\n❌ Error:'), error.message);
84
+ if (error.stack && process.env.DEBUG) {
85
+ console.error(chalk.gray(error.stack));
86
+ }
87
+ process.exit(1);
88
+ }
89
+ }
90
+
91
+ main().catch(error => {
92
+ console.error(chalk.red('Unexpected error:'), error);
93
+ process.exit(1);
94
+ });
packages/create-open-lovable/lib/installer.js ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { execSync } from 'child_process';
4
+ import chalk from 'chalk';
5
+ import inquirer from 'inquirer';
6
+ import { getEnvPrompts } from './prompts.js';
7
+
8
+ export async function installer(config) {
9
+ const { name, sandbox, path: installPath, skipInstall, dryRun, templatesDir } = config;
10
+ const projectPath = path.join(installPath, name);
11
+
12
+ if (dryRun) {
13
+ console.log(chalk.blue('\n📋 Dry run - would perform these actions:'));
14
+ console.log(chalk.gray(` - Create directory: ${projectPath}`));
15
+ console.log(chalk.gray(` - Copy base template files`));
16
+ console.log(chalk.gray(` - Copy ${sandbox}-specific files`));
17
+ console.log(chalk.gray(` - Create .env file`));
18
+ if (!skipInstall) {
19
+ console.log(chalk.gray(` - Run npm install`));
20
+ }
21
+ return;
22
+ }
23
+
24
+ // Check if directory exists
25
+ if (await fs.pathExists(projectPath)) {
26
+ const { overwrite } = await inquirer.prompt([{
27
+ type: 'confirm',
28
+ name: 'overwrite',
29
+ message: `Directory ${name} already exists. Overwrite?`,
30
+ default: false
31
+ }]);
32
+
33
+ if (!overwrite) {
34
+ throw new Error('Installation cancelled');
35
+ }
36
+ await fs.remove(projectPath);
37
+ }
38
+
39
+ // Create project directory
40
+ await fs.ensureDir(projectPath);
41
+
42
+ // Copy base template (shared files)
43
+ const baseTemplatePath = path.join(templatesDir, 'base');
44
+ if (await fs.pathExists(baseTemplatePath)) {
45
+ await copyTemplate(baseTemplatePath, projectPath);
46
+ } else {
47
+ // If no base template exists yet, copy from the main project
48
+ await copyMainProject(path.dirname(templatesDir), projectPath, sandbox);
49
+ }
50
+
51
+ // Copy provider-specific template
52
+ const providerTemplatePath = path.join(templatesDir, sandbox);
53
+ if (await fs.pathExists(providerTemplatePath)) {
54
+ await copyTemplate(providerTemplatePath, projectPath);
55
+ }
56
+
57
+ // Configure environment variables
58
+ if (config.configureEnv) {
59
+ const envAnswers = await inquirer.prompt(getEnvPrompts(sandbox));
60
+ await createEnvFile(projectPath, sandbox, envAnswers);
61
+ } else {
62
+ // Create .env.example copy
63
+ await createEnvExample(projectPath, sandbox);
64
+ }
65
+
66
+ // Update package.json with project name
67
+ await updatePackageJson(projectPath, name);
68
+
69
+ // Update configuration to use the selected sandbox provider
70
+ await updateAppConfig(projectPath, sandbox);
71
+
72
+ // Install dependencies
73
+ if (!skipInstall) {
74
+ console.log(chalk.cyan('\n📦 Installing dependencies...'));
75
+ execSync('npm install', {
76
+ cwd: projectPath,
77
+ stdio: 'inherit'
78
+ });
79
+ }
80
+ }
81
+
82
+ async function copyTemplate(src, dest) {
83
+ const files = await fs.readdir(src);
84
+
85
+ for (const file of files) {
86
+ const srcPath = path.join(src, file);
87
+ const destPath = path.join(dest, file);
88
+
89
+ const stat = await fs.stat(srcPath);
90
+
91
+ if (stat.isDirectory()) {
92
+ await fs.ensureDir(destPath);
93
+ await copyTemplate(srcPath, destPath);
94
+ } else {
95
+ await fs.copy(srcPath, destPath, { overwrite: true });
96
+ }
97
+ }
98
+ }
99
+
100
+ async function copyMainProject(mainProjectPath, projectPath, sandbox) {
101
+ // Copy essential directories and files from the main project
102
+ const itemsToCopy = [
103
+ 'app',
104
+ 'components',
105
+ 'config',
106
+ 'lib',
107
+ 'types',
108
+ 'public',
109
+ 'styles',
110
+ '.eslintrc.json',
111
+ '.gitignore',
112
+ 'next.config.js',
113
+ 'package.json',
114
+ 'tailwind.config.ts',
115
+ 'tsconfig.json',
116
+ 'postcss.config.mjs'
117
+ ];
118
+
119
+ for (const item of itemsToCopy) {
120
+ const srcPath = path.join(mainProjectPath, '..', item);
121
+ const destPath = path.join(projectPath, item);
122
+
123
+ if (await fs.pathExists(srcPath)) {
124
+ await fs.copy(srcPath, destPath, {
125
+ overwrite: true,
126
+ filter: (src) => {
127
+ // Skip node_modules and .next
128
+ if (src.includes('node_modules') || src.includes('.next')) {
129
+ return false;
130
+ }
131
+ return true;
132
+ }
133
+ });
134
+ }
135
+ }
136
+ }
137
+
138
+ async function createEnvFile(projectPath, sandbox, answers) {
139
+ let envContent = '# Open Lovable Configuration\n\n';
140
+
141
+ // Sandbox provider
142
+ envContent += `# Sandbox Provider\n`;
143
+ envContent += `SANDBOX_PROVIDER=${sandbox}\n\n`;
144
+
145
+ // Required keys
146
+ envContent += `# REQUIRED - Web scraping for cloning websites\n`;
147
+ envContent += `FIRECRAWL_API_KEY=${answers.firecrawlApiKey || 'your_firecrawl_api_key_here'}\n\n`;
148
+
149
+ if (sandbox === 'e2b') {
150
+ envContent += `# REQUIRED - E2B Sandboxes\n`;
151
+ envContent += `E2B_API_KEY=${answers.e2bApiKey || 'your_e2b_api_key_here'}\n\n`;
152
+ } else if (sandbox === 'vercel') {
153
+ envContent += `# REQUIRED - Vercel Sandboxes\n`;
154
+ if (answers.vercelAuthMethod === 'oidc') {
155
+ envContent += `# Using OIDC authentication (automatic in Vercel environment)\n`;
156
+ } else {
157
+ envContent += `VERCEL_TEAM_ID=${answers.vercelTeamId || 'your_team_id'}\n`;
158
+ envContent += `VERCEL_PROJECT_ID=${answers.vercelProjectId || 'your_project_id'}\n`;
159
+ envContent += `VERCEL_TOKEN=${answers.vercelToken || 'your_access_token'}\n`;
160
+ }
161
+ envContent += '\n';
162
+ }
163
+
164
+ // Optional AI provider keys
165
+ envContent += `# OPTIONAL - AI Providers\n`;
166
+
167
+ if (answers.anthropicApiKey) {
168
+ envContent += `ANTHROPIC_API_KEY=${answers.anthropicApiKey}\n`;
169
+ } else {
170
+ envContent += `# ANTHROPIC_API_KEY=your_anthropic_api_key_here\n`;
171
+ }
172
+
173
+ if (answers.openaiApiKey) {
174
+ envContent += `OPENAI_API_KEY=${answers.openaiApiKey}\n`;
175
+ } else {
176
+ envContent += `# OPENAI_API_KEY=your_openai_api_key_here\n`;
177
+ }
178
+
179
+ if (answers.geminiApiKey) {
180
+ envContent += `GEMINI_API_KEY=${answers.geminiApiKey}\n`;
181
+ } else {
182
+ envContent += `# GEMINI_API_KEY=your_gemini_api_key_here\n`;
183
+ }
184
+
185
+ if (answers.groqApiKey) {
186
+ envContent += `GROQ_API_KEY=${answers.groqApiKey}\n`;
187
+ } else {
188
+ envContent += `# GROQ_API_KEY=your_groq_api_key_here\n`;
189
+ }
190
+
191
+ await fs.writeFile(path.join(projectPath, '.env'), envContent);
192
+ await fs.writeFile(path.join(projectPath, '.env.example'), envContent.replace(/=.+/g, '=your_key_here'));
193
+ }
194
+
195
+ async function createEnvExample(projectPath, sandbox) {
196
+ let envContent = '# Open Lovable Configuration\n\n';
197
+
198
+ envContent += `# Sandbox Provider\n`;
199
+ envContent += `SANDBOX_PROVIDER=${sandbox}\n\n`;
200
+
201
+ envContent += `# REQUIRED - Web scraping for cloning websites\n`;
202
+ envContent += `# Get yours at https://firecrawl.dev\n`;
203
+ envContent += `FIRECRAWL_API_KEY=your_firecrawl_api_key_here\n\n`;
204
+
205
+ if (sandbox === 'e2b') {
206
+ envContent += `# REQUIRED - Sandboxes for code execution\n`;
207
+ envContent += `# Get yours at https://e2b.dev\n`;
208
+ envContent += `E2B_API_KEY=your_e2b_api_key_here\n\n`;
209
+ } else if (sandbox === 'vercel') {
210
+ envContent += `# REQUIRED - Vercel Sandboxes\n`;
211
+ envContent += `# Option 1: OIDC (automatic in Vercel environment)\n`;
212
+ envContent += `# Option 2: Personal Access Token\n`;
213
+ envContent += `VERCEL_TEAM_ID=your_team_id\n`;
214
+ envContent += `VERCEL_PROJECT_ID=your_project_id\n`;
215
+ envContent += `VERCEL_TOKEN=your_access_token\n\n`;
216
+ }
217
+
218
+ envContent += `# OPTIONAL - AI Providers (need at least one)\n`;
219
+ envContent += `# Get yours at https://console.anthropic.com\n`;
220
+ envContent += `ANTHROPIC_API_KEY=your_anthropic_api_key_here\n\n`;
221
+ envContent += `# Get yours at https://platform.openai.com\n`;
222
+ envContent += `OPENAI_API_KEY=your_openai_api_key_here\n\n`;
223
+ envContent += `# Get yours at https://aistudio.google.com/app/apikey\n`;
224
+ envContent += `GEMINI_API_KEY=your_gemini_api_key_here\n\n`;
225
+ envContent += `# Get yours at https://console.groq.com\n`;
226
+ envContent += `GROQ_API_KEY=your_groq_api_key_here\n`;
227
+
228
+ await fs.writeFile(path.join(projectPath, '.env.example'), envContent);
229
+ }
230
+
231
+ async function updatePackageJson(projectPath, name) {
232
+ const packageJsonPath = path.join(projectPath, 'package.json');
233
+
234
+ if (await fs.pathExists(packageJsonPath)) {
235
+ const packageJson = await fs.readJson(packageJsonPath);
236
+ packageJson.name = name;
237
+ await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
238
+ }
239
+ }
240
+
241
+ async function updateAppConfig(projectPath, sandbox) {
242
+ const configPath = path.join(projectPath, 'config', 'app.config.ts');
243
+
244
+ if (await fs.pathExists(configPath)) {
245
+ let content = await fs.readFile(configPath, 'utf-8');
246
+
247
+ // Add sandbox provider configuration
248
+ const sandboxConfig = `
249
+ // Sandbox Provider Configuration
250
+ sandboxProvider: process.env.SANDBOX_PROVIDER || '${sandbox}',
251
+ `;
252
+
253
+ // Insert after the opening of appConfig
254
+ content = content.replace(
255
+ 'export const appConfig = {',
256
+ `export const appConfig = {${sandboxConfig}`
257
+ );
258
+
259
+ await fs.writeFile(configPath, content);
260
+ }
261
+ }
packages/create-open-lovable/lib/prompts.js ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export function getPrompts(config) {
2
+ const prompts = [];
3
+
4
+ if (!config.name) {
5
+ prompts.push({
6
+ type: 'input',
7
+ name: 'name',
8
+ message: 'Project name:',
9
+ default: 'my-open-lovable',
10
+ validate: (input) => {
11
+ if (!input || input.trim() === '') {
12
+ return 'Project name is required';
13
+ }
14
+ if (!/^[a-z0-9-_]+$/i.test(input)) {
15
+ return 'Project name can only contain letters, numbers, hyphens, and underscores';
16
+ }
17
+ return true;
18
+ }
19
+ });
20
+ }
21
+
22
+ if (!config.sandbox) {
23
+ prompts.push({
24
+ type: 'list',
25
+ name: 'sandbox',
26
+ message: 'Choose your sandbox provider:',
27
+ choices: [
28
+ {
29
+ name: 'E2B - Full-featured development sandboxes',
30
+ value: 'e2b',
31
+ short: 'E2B'
32
+ },
33
+ {
34
+ name: 'Vercel - Lightweight ephemeral VMs',
35
+ value: 'vercel',
36
+ short: 'Vercel'
37
+ }
38
+ ],
39
+ default: 'e2b'
40
+ });
41
+ }
42
+
43
+ prompts.push({
44
+ type: 'confirm',
45
+ name: 'configureEnv',
46
+ message: 'Would you like to configure API keys now?',
47
+ default: true
48
+ });
49
+
50
+ return prompts;
51
+ }
52
+
53
+ export function getEnvPrompts(provider) {
54
+ const prompts = [];
55
+
56
+ // Always include Firecrawl API key
57
+ prompts.push({
58
+ type: 'input',
59
+ name: 'firecrawlApiKey',
60
+ message: 'Firecrawl API key (for web scraping):',
61
+ validate: (input) => {
62
+ if (!input || input.trim() === '') {
63
+ return 'Firecrawl API key is required for web scraping functionality';
64
+ }
65
+ return true;
66
+ }
67
+ });
68
+
69
+ if (provider === 'e2b') {
70
+ prompts.push({
71
+ type: 'input',
72
+ name: 'e2bApiKey',
73
+ message: 'E2B API key:',
74
+ validate: (input) => {
75
+ if (!input || input.trim() === '') {
76
+ return 'E2B API key is required';
77
+ }
78
+ return true;
79
+ }
80
+ });
81
+ } else if (provider === 'vercel') {
82
+ prompts.push({
83
+ type: 'list',
84
+ name: 'vercelAuthMethod',
85
+ message: 'Vercel authentication method:',
86
+ choices: [
87
+ {
88
+ name: 'OIDC Token (automatic in Vercel environment)',
89
+ value: 'oidc',
90
+ short: 'OIDC'
91
+ },
92
+ {
93
+ name: 'Personal Access Token',
94
+ value: 'pat',
95
+ short: 'PAT'
96
+ }
97
+ ]
98
+ });
99
+
100
+ prompts.push({
101
+ type: 'input',
102
+ name: 'vercelTeamId',
103
+ message: 'Vercel Team ID:',
104
+ when: (answers) => answers.vercelAuthMethod === 'pat',
105
+ validate: (input) => {
106
+ if (!input || input.trim() === '') {
107
+ return 'Team ID is required for PAT authentication';
108
+ }
109
+ return true;
110
+ }
111
+ });
112
+
113
+ prompts.push({
114
+ type: 'input',
115
+ name: 'vercelProjectId',
116
+ message: 'Vercel Project ID:',
117
+ when: (answers) => answers.vercelAuthMethod === 'pat',
118
+ validate: (input) => {
119
+ if (!input || input.trim() === '') {
120
+ return 'Project ID is required for PAT authentication';
121
+ }
122
+ return true;
123
+ }
124
+ });
125
+
126
+ prompts.push({
127
+ type: 'input',
128
+ name: 'vercelToken',
129
+ message: 'Vercel Access Token:',
130
+ when: (answers) => answers.vercelAuthMethod === 'pat',
131
+ validate: (input) => {
132
+ if (!input || input.trim() === '') {
133
+ return 'Access token is required for PAT authentication';
134
+ }
135
+ return true;
136
+ }
137
+ });
138
+ }
139
+
140
+ // Optional AI provider keys
141
+ prompts.push({
142
+ type: 'confirm',
143
+ name: 'addAiKeys',
144
+ message: 'Would you like to add AI provider API keys?',
145
+ default: true
146
+ });
147
+
148
+ prompts.push({
149
+ type: 'checkbox',
150
+ name: 'aiProviders',
151
+ message: 'Select AI providers to configure:',
152
+ when: (answers) => answers.addAiKeys,
153
+ choices: [
154
+ { name: 'Anthropic (Claude)', value: 'anthropic' },
155
+ { name: 'OpenAI (GPT)', value: 'openai' },
156
+ { name: 'Google (Gemini)', value: 'gemini' },
157
+ { name: 'Groq', value: 'groq' }
158
+ ]
159
+ });
160
+
161
+ prompts.push({
162
+ type: 'input',
163
+ name: 'anthropicApiKey',
164
+ message: 'Anthropic API key:',
165
+ when: (answers) => answers.aiProviders && answers.aiProviders.includes('anthropic')
166
+ });
167
+
168
+ prompts.push({
169
+ type: 'input',
170
+ name: 'openaiApiKey',
171
+ message: 'OpenAI API key:',
172
+ when: (answers) => answers.aiProviders && answers.aiProviders.includes('openai')
173
+ });
174
+
175
+ prompts.push({
176
+ type: 'input',
177
+ name: 'geminiApiKey',
178
+ message: 'Gemini API key:',
179
+ when: (answers) => answers.aiProviders && answers.aiProviders.includes('gemini')
180
+ });
181
+
182
+ prompts.push({
183
+ type: 'input',
184
+ name: 'groqApiKey',
185
+ message: 'Groq API key:',
186
+ when: (answers) => answers.aiProviders && answers.aiProviders.includes('groq')
187
+ });
188
+
189
+ return prompts;
190
+ }
packages/create-open-lovable/package.json ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "create-open-lovable",
3
+ "version": "1.0.0",
4
+ "description": "CLI to bootstrap Open Lovable with your choice of sandbox provider",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "create-open-lovable": "./index.js"
9
+ },
10
+ "scripts": {
11
+ "test": "node index.js --dry-run"
12
+ },
13
+ "keywords": [
14
+ "lovable",
15
+ "sandbox",
16
+ "e2b",
17
+ "vercel",
18
+ "ai",
19
+ "code-generation"
20
+ ],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "dependencies": {
24
+ "chalk": "^5.3.0",
25
+ "commander": "^11.1.0",
26
+ "fs-extra": "^11.2.0",
27
+ "inquirer": "^9.2.12",
28
+ "ora": "^7.0.1"
29
+ },
30
+ "engines": {
31
+ "node": ">=16.0.0"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ }
36
+ }
packages/create-open-lovable/templates/e2b/.env.example ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Lovable Configuration - E2B Provider
2
+
3
+ # Sandbox Provider
4
+ SANDBOX_PROVIDER=e2b
5
+
6
+ # REQUIRED - Sandboxes for code execution
7
+ # Get yours at https://e2b.dev
8
+ E2B_API_KEY=your_e2b_api_key_here
9
+
10
+ # REQUIRED - Web scraping for cloning websites
11
+ # Get yours at https://firecrawl.dev
12
+ FIRECRAWL_API_KEY=your_firecrawl_api_key_here
13
+
14
+ # OPTIONAL - AI Providers (need at least one)
15
+ # Get yours at https://console.anthropic.com
16
+ ANTHROPIC_API_KEY=your_anthropic_api_key_here
17
+
18
+ # Get yours at https://platform.openai.com
19
+ OPENAI_API_KEY=your_openai_api_key_here
20
+
21
+ # Get yours at https://aistudio.google.com/app/apikey
22
+ GEMINI_API_KEY=your_gemini_api_key_here
23
+
24
+ # Get yours at https://console.groq.com
25
+ GROQ_API_KEY=your_groq_api_key_here
packages/create-open-lovable/templates/e2b/README.md ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Lovable - E2B Sandbox
2
+
3
+ This project is configured to use E2B sandboxes for code execution.
4
+
5
+ ## Setup
6
+
7
+ 1. Get your E2B API key from [https://e2b.dev](https://e2b.dev)
8
+ 2. Get your Firecrawl API key from [https://firecrawl.dev](https://firecrawl.dev)
9
+ 3. Copy `.env.example` to `.env` and add your API keys
10
+ 4. Run `npm install` to install dependencies
11
+ 5. Run `npm run dev` to start the development server
12
+
13
+ ## E2B Features
14
+
15
+ - Full-featured development sandboxes
16
+ - 15-minute default timeout (configurable)
17
+ - Persistent file system during session
18
+ - Support for complex package installations
19
+ - Built-in Python runtime for code execution
20
+
21
+ ## Configuration
22
+
23
+ You can adjust E2B settings in `config/app.config.ts`:
24
+
25
+ - `timeoutMinutes`: Sandbox session timeout (default: 15)
26
+ - `vitePort`: Development server port (default: 5173)
27
+ - `viteStartupDelay`: Time to wait for Vite to start (default: 7000ms)
28
+
29
+ ## Troubleshooting
30
+
31
+ If you encounter issues:
32
+
33
+ 1. Verify your E2B API key is valid
34
+ 2. Check the console for detailed error messages
35
+ 3. Ensure you have a stable internet connection
36
+ 4. Try refreshing the page and creating a new sandbox
37
+
38
+ For more help, visit the [E2B documentation](https://docs.e2b.dev).
packages/create-open-lovable/templates/vercel/.env.example ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Lovable Configuration - Vercel Provider
2
+
3
+ # Sandbox Provider
4
+ SANDBOX_PROVIDER=vercel
5
+
6
+ # REQUIRED - Vercel Sandboxes
7
+ # Option 1: OIDC Token (automatic in Vercel environment)
8
+ # Option 2: Personal Access Token (configure below)
9
+ VERCEL_TEAM_ID=your_team_id
10
+ VERCEL_PROJECT_ID=your_project_id
11
+ VERCEL_TOKEN=your_access_token
12
+
13
+ # REQUIRED - Web scraping for cloning websites
14
+ # Get yours at https://firecrawl.dev
15
+ FIRECRAWL_API_KEY=your_firecrawl_api_key_here
16
+
17
+ # OPTIONAL - AI Providers (need at least one)
18
+ # Get yours at https://console.anthropic.com
19
+ ANTHROPIC_API_KEY=your_anthropic_api_key_here
20
+
21
+ # Get yours at https://platform.openai.com
22
+ OPENAI_API_KEY=your_openai_api_key_here
23
+
24
+ # Get yours at https://aistudio.google.com/app/apikey
25
+ GEMINI_API_KEY=your_gemini_api_key_here
26
+
27
+ # Get yours at https://console.groq.com
28
+ GROQ_API_KEY=your_groq_api_key_here
packages/create-open-lovable/templates/vercel/README.md ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Lovable - Vercel Sandbox
2
+
3
+ This project is configured to use Vercel Sandboxes for code execution.
4
+
5
+ ## Setup
6
+
7
+ 1. Configure Vercel authentication (see below)
8
+ 2. Get your Firecrawl API key from [https://firecrawl.dev](https://firecrawl.dev)
9
+ 3. Copy `.env.example` to `.env` and add your credentials
10
+ 4. Run `npm install` to install dependencies
11
+ 5. Run `npm run dev` to start the development server
12
+
13
+ ## Vercel Authentication
14
+
15
+ ### Option 1: OIDC Token (Automatic for Vercel deployments)
16
+ When running in a Vercel environment, authentication happens automatically via OIDC tokens. No configuration needed!
17
+
18
+ ### Option 2: Personal Access Token (For local development)
19
+ 1. Create a Personal Access Token in your [Vercel account settings](https://vercel.com/account/tokens)
20
+ 2. Get your Team ID from your [team settings](https://vercel.com/teams)
21
+ 3. Create a project and get the Project ID
22
+ 4. Add these to your `.env` file:
23
+ - `VERCEL_TOKEN`
24
+ - `VERCEL_TEAM_ID`
25
+ - `VERCEL_PROJECT_ID`
26
+
27
+ ## Vercel Sandbox Features
28
+
29
+ - Lightweight ephemeral Linux VMs
30
+ - Powered by Firecracker MicroVMs
31
+ - 5-minute default timeout (max 45 minutes)
32
+ - 8 vCPUs maximum
33
+ - Root access for package installation
34
+ - Node 22 runtime included
35
+
36
+ ## Configuration
37
+
38
+ You can adjust Vercel settings in `config/app.config.ts`:
39
+
40
+ - `maxDuration`: Sandbox session timeout (default: 5 minutes)
41
+ - Authentication method (OIDC or PAT)
42
+
43
+ ## Troubleshooting
44
+
45
+ If you encounter issues:
46
+
47
+ 1. Verify your authentication credentials
48
+ 2. Check if you're using the correct authentication method
49
+ 3. Ensure your Vercel account has sandbox access
50
+ 4. Check the console for detailed error messages
51
+
52
+ For more help, visit the [Vercel Sandbox documentation](https://vercel.com/docs/vercel-sandbox).
pnpm-lock.yaml ADDED
The diff for this file is too large to render. See raw diff
 
postcss.config.mjs ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ export default {
2
+ plugins: {
3
+ "postcss-import": {},
4
+ "postcss-nesting": {},
5
+ "tailwindcss": {},
6
+ "autoprefixer": {},
7
+ },
8
+ };
public/compressor.json ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "configs": [
3
+ {
4
+ "extension": "avif",
5
+ "quality": 80,
6
+ "effort": 4,
7
+ "scale": 2
8
+ },
9
+ {
10
+ "extension": "webp",
11
+ "quality": 85,
12
+ "effort": 4,
13
+ "scale": 2
14
+ }
15
+ ]
16
+ }
public/file.svg ADDED
public/firecrawl-logo ADDED
Binary file (2.02 kB). View file
 
public/firecrawl.svg ADDED
public/globe.svg ADDED
public/window.svg ADDED
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ openai>=1.65.4
2
+ vllm>=0.6.4.post1
3
+ transformers>=4.46.3
4
+ tiktoken>=0.9.0
scripts/run.sh ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # export OPENAI_API_KEY=""
2
+
3
+ GPT_VERSION="o3-mini"
4
+
5
+ PAPER_NAME="Transformer"
6
+ PDF_PATH="../examples/Transformer.pdf" # .pdf
7
+ PDF_JSON_PATH="../examples/Transformer.json" # .json
8
+ PDF_JSON_CLEANED_PATH="../examples/Transformer_cleaned.json" # _cleaned.json
9
+ OUTPUT_DIR="../outputs/Transformer"
10
+ OUTPUT_REPO_DIR="../outputs/Transformer_repo"
11
+
12
+ mkdir -p $OUTPUT_DIR
13
+ mkdir -p $OUTPUT_REPO_DIR
14
+
15
+ echo $PAPER_NAME
16
+
17
+ echo "------- Preprocess -------"
18
+
19
+ python ../codes/0_pdf_process.py \
20
+ --input_json_path ${PDF_JSON_PATH} \
21
+ --output_json_path ${PDF_JSON_CLEANED_PATH} \
22
+
23
+
24
+ echo "------- PaperCoder -------"
25
+
26
+ python ../codes/1_planning.py \
27
+ --paper_name $PAPER_NAME \
28
+ --gpt_version ${GPT_VERSION} \
29
+ --pdf_json_path ${PDF_JSON_CLEANED_PATH} \
30
+ --output_dir ${OUTPUT_DIR}
31
+
32
+
33
+ python ../codes/1.1_extract_config.py \
34
+ --paper_name $PAPER_NAME \
35
+ --output_dir ${OUTPUT_DIR}
36
+
37
+ cp -rp ${OUTPUT_DIR}/planning_config.yaml ${OUTPUT_REPO_DIR}/config.yaml
38
+
39
+ python ../codes/2_analyzing.py \
40
+ --paper_name $PAPER_NAME \
41
+ --gpt_version ${GPT_VERSION} \
42
+ --pdf_json_path ${PDF_JSON_CLEANED_PATH} \
43
+ --output_dir ${OUTPUT_DIR}
44
+
45
+ python ../codes/3_coding.py \
46
+ --paper_name $PAPER_NAME \
47
+ --gpt_version ${GPT_VERSION} \
48
+ --pdf_json_path ${PDF_JSON_CLEANED_PATH} \
49
+ --output_dir ${OUTPUT_DIR} \
50
+ --output_repo_dir ${OUTPUT_REPO_DIR} \
scripts/run_latex.sh ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # export OPENAI_API_KEY=""
2
+
3
+ GPT_VERSION="o3-mini"
4
+
5
+ PAPER_NAME="Transformer"
6
+ PDF_LATEX_CLEANED_PATH="../examples/Transformer_cleaned.tex" # _cleaned.tex
7
+ OUTPUT_DIR="../outputs/Transformer"
8
+ OUTPUT_REPO_DIR="../outputs/Transformer_repo"
9
+
10
+ mkdir -p $OUTPUT_DIR
11
+ mkdir -p $OUTPUT_REPO_DIR
12
+
13
+ echo $PAPER_NAME
14
+
15
+ echo "------- PaperCoder -------"
16
+
17
+ python ../codes/1_planning.py \
18
+ --paper_name $PAPER_NAME \
19
+ --gpt_version ${GPT_VERSION} \
20
+ --pdf_latex_path ${PDF_LATEX_CLEANED_PATH} \
21
+ --paper_format LaTeX \
22
+ --output_dir ${OUTPUT_DIR}
23
+
24
+
25
+ python ../codes/1.1_extract_config.py \
26
+ --paper_name $PAPER_NAME \
27
+ --output_dir ${OUTPUT_DIR}
28
+
29
+ cp -rp ${OUTPUT_DIR}/planning_config.yaml ${OUTPUT_REPO_DIR}/config.yaml
30
+
31
+ python ../codes/2_analyzing.py \
32
+ --paper_name $PAPER_NAME \
33
+ --gpt_version ${GPT_VERSION} \
34
+ --pdf_latex_path ${PDF_LATEX_CLEANED_PATH} \
35
+ --paper_format LaTeX \
36
+ --output_dir ${OUTPUT_DIR}
37
+
38
+ python ../codes/3_coding.py \
39
+ --paper_name $PAPER_NAME \
40
+ --gpt_version ${GPT_VERSION} \
41
+ --pdf_latex_path ${PDF_LATEX_CLEANED_PATH} \
42
+ --paper_format LaTeX \
43
+ --output_dir ${OUTPUT_DIR} \
44
+ --output_repo_dir ${OUTPUT_REPO_DIR} \
scripts/run_latex_llm.sh ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL_NAME="deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct"
2
+ TP_SIZE=2
3
+
4
+ PAPER_NAME="Transformer"
5
+ PDF_LATEX_CLEANED_PATH="../examples/Transformer_cleaned.tex" # _cleaned.tex
6
+ OUTPUT_DIR="../outputs/Transformer_dscoder"
7
+ OUTPUT_REPO_DIR="../outputs/Transformer_dscoder_repo"
8
+
9
+ mkdir -p $OUTPUT_DIR
10
+ mkdir -p $OUTPUT_REPO_DIR
11
+
12
+ echo $PAPER_NAME
13
+
14
+ echo "------- PaperCoder -------"
15
+
16
+ python ../codes/1_planning_llm.py \
17
+ --paper_name $PAPER_NAME \
18
+ --model_name ${MODEL_NAME} \
19
+ --tp_size ${TP_SIZE} \
20
+ --pdf_latex_path ${PDF_LATEX_CLEANED_PATH} \
21
+ --paper_format LaTeX \
22
+ --output_dir ${OUTPUT_DIR}
23
+
24
+ python ../codes/1.1_extract_config.py \
25
+ --paper_name $PAPER_NAME \
26
+ --output_dir ${OUTPUT_DIR}
27
+
28
+ cp -rp ${OUTPUT_DIR}/planning_config.yaml ${OUTPUT_REPO_DIR}/config.yaml
29
+
30
+ python ../codes/2_analyzing_llm.py \
31
+ --paper_name $PAPER_NAME \
32
+ --model_name ${MODEL_NAME} \
33
+ --tp_size ${TP_SIZE} \
34
+ --pdf_latex_path ${PDF_LATEX_CLEANED_PATH} \
35
+ --paper_format LaTeX \
36
+ --output_dir ${OUTPUT_DIR}
37
+
38
+ python ../codes/3_coding_llm.py \
39
+ --paper_name $PAPER_NAME \
40
+ --model_name ${MODEL_NAME} \
41
+ --tp_size ${TP_SIZE} \
42
+ --pdf_latex_path ${PDF_LATEX_CLEANED_PATH} \
43
+ --paper_format LaTeX \
44
+ --output_dir ${OUTPUT_DIR} \
45
+ --output_repo_dir ${OUTPUT_REPO_DIR} \
scripts/run_llm.sh ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MODEL_NAME="deepseek-ai/DeepSeek-Coder-V2-Lite-Instruct"
2
+ TP_SIZE=2
3
+
4
+ PAPER_NAME="Transformer"
5
+ PDF_PATH="../examples/Transformer.pdf" # .pdf
6
+ PDF_JSON_PATH="../examples/Transformer.json" # .json
7
+ PDF_JSON_CLEANED_PATH="../examples/Transformer_cleaned.json" # _cleaned.json
8
+ OUTPUT_DIR="../outputs/Transformer_dscoder"
9
+ OUTPUT_REPO_DIR="../outputs/Transformer_dscoder_repo"
10
+
11
+ mkdir -p $OUTPUT_DIR
12
+ mkdir -p $OUTPUT_REPO_DIR
13
+
14
+ echo $PAPER_NAME
15
+
16
+ echo "------- Preprocess -------"
17
+
18
+ python ../codes/0_pdf_process.py \
19
+ --input_json_path ${PDF_JSON_PATH} \
20
+ --output_json_path ${PDF_JSON_CLEANED_PATH} \
21
+
22
+
23
+ echo "------- PaperCoder -------"
24
+
25
+ python ../codes/1_planning_llm.py \
26
+ --paper_name $PAPER_NAME \
27
+ --model_name ${MODEL_NAME} \
28
+ --tp_size ${TP_SIZE} \
29
+ --pdf_json_path ${PDF_JSON_CLEANED_PATH} \
30
+ --output_dir ${OUTPUT_DIR}
31
+
32
+ python ../codes/1.1_extract_config.py \
33
+ --paper_name $PAPER_NAME \
34
+ --output_dir ${OUTPUT_DIR}
35
+
36
+ cp -rp ${OUTPUT_DIR}/planning_config.yaml ${OUTPUT_REPO_DIR}/config.yaml
37
+
38
+ python ../codes/2_analyzing_llm.py \
39
+ --paper_name $PAPER_NAME \
40
+ --model_name ${MODEL_NAME} \
41
+ --tp_size ${TP_SIZE} \
42
+ --pdf_json_path ${PDF_JSON_CLEANED_PATH} \
43
+ --output_dir ${OUTPUT_DIR}
44
+
45
+ python ../codes/3_coding_llm.py \
46
+ --paper_name $PAPER_NAME \
47
+ --model_name ${MODEL_NAME} \
48
+ --tp_size ${TP_SIZE} \
49
+ --pdf_json_path ${PDF_JSON_CLEANED_PATH} \
50
+ --output_dir ${OUTPUT_DIR} \
51
+ --output_repo_dir ${OUTPUT_REPO_DIR} \
styles/additional-styles/custom-fonts.css ADDED
File without changes
styles/additional-styles/theme.css ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .form-input:focus,
2
+ .form-textarea:focus,
3
+ .form-multiselect:focus,
4
+ .form-select:focus,
5
+ .form-checkbox:focus,
6
+ .form-radio:focus {
7
+ @apply ring-0;
8
+ }
9
+
10
+ /* Hamburger button */
11
+ .hamburger svg > *:nth-child(1),
12
+ .hamburger svg > *:nth-child(2),
13
+ .hamburger svg > *:nth-child(3) {
14
+ transform-origin: center;
15
+ transform: rotate(0deg);
16
+ }
17
+
18
+ .hamburger svg > *:nth-child(1) {
19
+ transition:
20
+ y 0.1s 0.25s ease-in,
21
+ transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19),
22
+ opacity 0.1s ease-in;
23
+ }
24
+
25
+ .hamburger svg > *:nth-child(2) {
26
+ transition: transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19);
27
+ }
28
+
29
+ .hamburger svg > *:nth-child(3) {
30
+ transition:
31
+ y 0.1s 0.25s ease-in,
32
+ transform 0.22s cubic-bezier(0.55, 0.055, 0.675, 0.19),
33
+ width 0.1s 0.25s ease-in;
34
+ }
35
+
36
+ .hamburger.active svg > *:nth-child(1) {
37
+ opacity: 0;
38
+ y: 11;
39
+ transform: rotate(225deg);
40
+ transition:
41
+ y 0.1s ease-out,
42
+ transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1),
43
+ opacity 0.1s 0.12s ease-out;
44
+ }
45
+
46
+ .hamburger.active svg > *:nth-child(2) {
47
+ transform: rotate(225deg);
48
+ transition: transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1);
49
+ }
50
+
51
+ .hamburger.active svg > *:nth-child(3) {
52
+ y: 11;
53
+ transform: rotate(135deg);
54
+ transition:
55
+ y 0.1s ease-out,
56
+ transform 0.22s 0.12s cubic-bezier(0.215, 0.61, 0.355, 1),
57
+ width 0.1s ease-out;
58
+ }
styles/additional-styles/utility-patterns.css ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Typography */
2
+ .h1 {
3
+ @apply text-5xl font-bold;
4
+ }
5
+
6
+ .h2 {
7
+ @apply text-4xl font-bold;
8
+ }
9
+
10
+ .h3 {
11
+ @apply text-3xl font-bold;
12
+ }
13
+
14
+ .h4 {
15
+ @apply text-2xl font-bold;
16
+ }
17
+
18
+ @screen md {
19
+ .h1 {
20
+ @apply text-6xl;
21
+ }
22
+
23
+ .h2 {
24
+ @apply text-5xl;
25
+ }
26
+
27
+ .h3 {
28
+ @apply text-4xl;
29
+ }
30
+ }
31
+
32
+ /* Buttons */
33
+ .btn,
34
+ .btn-sm {
35
+ @apply text-sm font-medium inline-flex items-center justify-center border border-transparent rounded-md tracking-normal transition;
36
+ }
37
+
38
+ .btn {
39
+ @apply px-4 py-2;
40
+ }
41
+
42
+ .btn-sm {
43
+ @apply px-2 py-1;
44
+ }
45
+
46
+ /* Forms */
47
+ input[type="search"]::-webkit-search-decoration,
48
+ input[type="search"]::-webkit-search-cancel-button,
49
+ input[type="search"]::-webkit-search-results-button,
50
+ input[type="search"]::-webkit-search-results-decoration {
51
+ -webkit-appearance: none;
52
+ }
53
+
54
+ .form-input,
55
+ .form-textarea,
56
+ .form-multiselect,
57
+ .form-select,
58
+ .form-checkbox,
59
+ .form-radio {
60
+ @apply bg-white border border-zinc-200 focus:border-zinc-400 shadow shadow-black/5;
61
+ }
62
+
63
+ .form-input,
64
+ .form-textarea,
65
+ .form-multiselect,
66
+ .form-select,
67
+ .form-checkbox {
68
+ @apply rounded;
69
+ }
70
+
71
+ .form-input,
72
+ .form-textarea,
73
+ .form-multiselect,
74
+ .form-select {
75
+ @apply text-zinc-600 text-sm px-4 py-2;
76
+ }
77
+
78
+ .form-input,
79
+ .form-textarea {
80
+ @apply placeholder-zinc-400;
81
+ }
82
+
83
+ .form-select {
84
+ @apply pr-10;
85
+ }
86
+
87
+ .form-checkbox,
88
+ .form-radio {
89
+ @apply text-zinc-800;
90
+ }
91
+
92
+ .form-checkbox {
93
+ @apply rounded-sm;
94
+ }
95
+
96
+ /* Fix input cursor visibility on Windows Chrome */
97
+ input,
98
+ textarea {
99
+ caret-color: hsl(var(--foreground));
100
+ }
101
+
102
+ /* Chrome, Safari and Opera */
103
+ .no-scrollbar::-webkit-scrollbar {
104
+ display: none;
105
+ }
106
+
107
+ .no-scrollbar {
108
+ -ms-overflow-style: none;
109
+ /* IE and Edge */
110
+ scrollbar-width: none;
111
+ /* Firefox */
112
+ }
styles/chrome-bug.css ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Chrome has a bug with transitions on load since 2012!
3
+ *
4
+ * To prevent a "pop" of content, you have to disable all transitions until
5
+ * the page is done loading.
6
+ *
7
+ * https://lab.laukstein.com/bug/input
8
+ * https://twitter.com/timer150/status/1345217126680899584
9
+ */
10
+ body.loading * {
11
+ transition: none !important;
12
+ }
styles/colors.json ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "heat-4": {
3
+ "hex": "fa5d190a",
4
+ "p3": "0.980392 0.364706 0.098039 / 0.039216"
5
+ },
6
+ "heat-8": {
7
+ "hex": "fa5d1914",
8
+ "p3": "0.980392 0.364706 0.098039 / 0.078431"
9
+ },
10
+ "heat-12": {
11
+ "hex": "fa5d191f",
12
+ "p3": "0.980392 0.364706 0.098039 / 0.121569"
13
+ },
14
+ "heat-16": {
15
+ "hex": "fa5d1929",
16
+ "p3": "0.980392 0.364706 0.098039 / 0.160784"
17
+ },
18
+ "heat-20": {
19
+ "hex": "fa5d1933",
20
+ "p3": "0.980392 0.364706 0.098039 / 0.200000"
21
+ },
22
+ "heat-40": {
23
+ "hex": "fa5d1966",
24
+ "p3": "0.980392 0.364706 0.098039 / 0.400000"
25
+ },
26
+ "heat-90": {
27
+ "hex": "fa5d19e6",
28
+ "p3": "0.980392 0.364706 0.098039 / 0.900000"
29
+ },
30
+ "heat-100": {
31
+ "hex": "fa5d19ff",
32
+ "p3": "0.980392 0.364706 0.098039 / 1.000000"
33
+ },
34
+ "accent-black": {
35
+ "hex": "262626ff",
36
+ "p3": "0.149020 0.149020 0.149020 / 1.000000"
37
+ },
38
+ "accent-white": {
39
+ "hex": "ffffffff",
40
+ "p3": "1.000000 1.000000 1.000000 / 1.000000"
41
+ },
42
+ "accent-amethyst": {
43
+ "hex": "9061ffff",
44
+ "p3": "0.564706 0.380392 1.000000 / 1.000000"
45
+ },
46
+ "accent-bluetron": {
47
+ "hex": "2a6dfbff",
48
+ "p3": "0.164706 0.427451 0.984314 / 1.000000"
49
+ },
50
+ "accent-crimson": {
51
+ "hex": "eb3424ff",
52
+ "p3": "0.921569 0.203922 0.141176 / 1.000000"
53
+ },
54
+ "accent-forest": {
55
+ "hex": "42c366ff",
56
+ "p3": "0.258824 0.764706 0.400000 / 1.000000"
57
+ },
58
+ "accent-honey": {
59
+ "hex": "ecb730ff",
60
+ "p3": "0.925490 0.717647 0.188235 / 1.000000"
61
+ },
62
+ "black-alpha-1": {
63
+ "hex": "00000003",
64
+ "p3": "0.000000 0.000000 0.000000 / 0.011765"
65
+ },
66
+ "black-alpha-2": {
67
+ "hex": "00000005",
68
+ "p3": "0.000000 0.000000 0.000000 / 0.019608"
69
+ },
70
+ "black-alpha-3": {
71
+ "hex": "00000008",
72
+ "p3": "0.000000 0.000000 0.000000 / 0.031373"
73
+ },
74
+ "black-alpha-4": {
75
+ "hex": "0000000a",
76
+ "p3": "0.000000 0.000000 0.000000 / 0.039216"
77
+ },
78
+ "black-alpha-5": {
79
+ "hex": "0000000d",
80
+ "p3": "0.000000 0.000000 0.000000 / 0.050980"
81
+ },
82
+ "black-alpha-6": {
83
+ "hex": "0000000f",
84
+ "p3": "0.000000 0.000000 0.000000 / 0.058824"
85
+ },
86
+ "black-alpha-7": {
87
+ "hex": "00000012",
88
+ "p3": "0.000000 0.000000 0.000000 / 0.070588"
89
+ },
90
+ "black-alpha-8": {
91
+ "hex": "00000014",
92
+ "p3": "0.000000 0.000000 0.000000 / 0.078431"
93
+ },
94
+ "black-alpha-10": {
95
+ "hex": "0000001a",
96
+ "p3": "0.000000 0.000000 0.000000 / 0.101961"
97
+ },
98
+ "black-alpha-12": {
99
+ "hex": "0000001f",
100
+ "p3": "0.000000 0.000000 0.000000 / 0.121569"
101
+ },
102
+ "black-alpha-16": {
103
+ "hex": "00000029",
104
+ "p3": "0.000000 0.000000 0.000000 / 0.160784"
105
+ },
106
+ "black-alpha-20": {
107
+ "hex": "00000033",
108
+ "p3": "0.000000 0.000000 0.000000 / 0.200000"
109
+ },
110
+ "black-alpha-24": {
111
+ "hex": "0000003d",
112
+ "p3": "0.000000 0.000000 0.000000 / 0.239216"
113
+ },
114
+ "black-alpha-32": {
115
+ "hex": "26262652",
116
+ "p3": "0.149020 0.149020 0.149020 / 0.321569"
117
+ },
118
+ "black-alpha-40": {
119
+ "hex": "26262666",
120
+ "p3": "0.149020 0.149020 0.149020 / 0.400000"
121
+ },
122
+ "black-alpha-48": {
123
+ "hex": "2626267a",
124
+ "p3": "0.149020 0.149020 0.149020 / 0.478431"
125
+ },
126
+ "black-alpha-56": {
127
+ "hex": "2626268f",
128
+ "p3": "0.149020 0.149020 0.149020 / 0.560784"
129
+ },
130
+ "black-alpha-64": {
131
+ "hex": "262626a3",
132
+ "p3": "0.149020 0.149020 0.149020 / 0.639216"
133
+ },
134
+ "black-alpha-72": {
135
+ "hex": "262626b8",
136
+ "p3": "0.149020 0.149020 0.149020 / 0.721569"
137
+ },
138
+ "black-alpha-88": {
139
+ "hex": "262626e0",
140
+ "p3": "0.149020 0.149020 0.149020 / 0.878431"
141
+ },
142
+ "white-alpha-56": {
143
+ "hex": "ffffff8f",
144
+ "p3": "1.000000 1.000000 1.000000 / 0.560784"
145
+ },
146
+ "white-alpha-72": {
147
+ "hex": "ffffffb8",
148
+ "p3": "1.000000 1.000000 1.000000 / 0.721569"
149
+ },
150
+ "border-faint": {
151
+ "hex": "edededff",
152
+ "p3": "0.929412 0.929412 0.929412 / 1.000000"
153
+ },
154
+ "border-muted": {
155
+ "hex": "e8e8e8ff",
156
+ "p3": "0.909804 0.909804 0.909804 / 1.000000"
157
+ },
158
+ "border-loud": {
159
+ "hex": "e6e6e6ff",
160
+ "p3": "0.901961 0.901961 0.901961 / 1.000000"
161
+ },
162
+ "illustrations-faint": {
163
+ "hex": "edededff",
164
+ "p3": "0.929412 0.929412 0.929412 / 1.000000"
165
+ },
166
+ "illustrations-muted": {
167
+ "hex": "e6e6e6ff",
168
+ "p3": "0.901961 0.901961 0.901961 / 1.000000"
169
+ },
170
+ "illustrations-default": {
171
+ "hex": "dbdbdbff",
172
+ "p3": "0.858824 0.858824 0.858824 / 1.000000"
173
+ },
174
+ "background-lighter": {
175
+ "hex": "fbfbfbff",
176
+ "p3": "0.984314 0.984314 0.984314 / 1.000000"
177
+ },
178
+ "background-base": {
179
+ "hex": "f9f9f9ff",
180
+ "p3": "0.976471 0.976471 0.976471 / 1.000000"
181
+ }
182
+ }
styles/components/.cursor/rules/component-styles.md ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Component Styles Rules
2
+
3
+ When working with component-specific CSS in styles/components:
4
+
5
+ ## Architecture
6
+ Each component that requires custom CSS has a corresponding file:
7
+ - `button.css` - Fire-inspired button shadows and effects
8
+ - `modal.css` - Modal animations and backdrop effects
9
+ - `spinner.css` - Custom loading animations
10
+
11
+ ## Import Strategy
12
+ All component CSS files are imported in `styles/main.css`:
13
+ ```css
14
+ /* Component styles */
15
+ @import "./components/button.css";
16
+ @import "./components/modal.css";
17
+ @import "./components/spinner.css";
18
+ ```
19
+
20
+ ## Guidelines
21
+ 1. **Only create CSS files for components that need them** - If Tailwind utilities suffice, don't create a CSS file
22
+ 2. **Use P3 colors with sRGB fallbacks** - Ensure wide gamut displays get enhanced colors
23
+ 3. **Keep animations performant** - Use transform and opacity for animations
24
+ 4. **Component classes should be prefixed** - e.g., `.button-primary`, `.modal-backdrop`
25
+
26
+ ## P3 Color Example
27
+ ```css
28
+ .button-primary {
29
+ background: #fa5d19; /* sRGB fallback */
30
+ background: color(display-p3 0.9816 0.3634 0.0984); /* P3 color */
31
+ }
32
+ ```
styles/components/button.css ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .button {
2
+ transition: all 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
3
+ scale 0.1s cubic-bezier(0.25, 0.1, 0.25, 1),
4
+ box-shadow 0.1s cubic-bezier(0.25, 0.1, 0.25, 1);
5
+ }
6
+
7
+ .button:active {
8
+ transition: all 0.2s cubic-bezier(0.25, 0.1, 0.25, 1),
9
+ scale 0.05s cubic-bezier(0.25, 0.1, 0.25, 1),
10
+ box-shadow 0.05s cubic-bezier(0.25, 0.1, 0.25, 1);
11
+ }
12
+
13
+ .button-primary {
14
+ background: #ff4c00;
15
+ background: color(display-p3 0.9816 0.3634 0.0984);
16
+
17
+ box-shadow: 0px -6px 12px 0px rgba(255, 0, 0, 0.2) inset,
18
+ 0px 2px 4px 0px rgba(255, 77, 0, 0.12),
19
+ 0px 1px 1px 0px rgba(255, 77, 0, 0.12),
20
+ 0px 0.5px 0.5px 0px rgba(255, 77, 0, 0.16),
21
+ 0px 0.25px 0.25px 0px rgba(255, 77, 0, 0.2);
22
+
23
+ box-shadow: 0px -6px 12px 0px color(display-p3 0.9804 0.1127 0.098 / 0.2) inset,
24
+ 0px 2px 4px 0px color(display-p3 0.9804 0.3647 0.098 / 0.12),
25
+ 0px 1px 1px 0px color(display-p3 0.9804 0.3647 0.098 / 0.12),
26
+ 0px 0.5px 0.5px 0px color(display-p3 0.9804 0.3647 0.098 / 0.16),
27
+ 0px 0.25px 0.25px 0px color(display-p3 0.9804 0.3647 0.098 / 0.2);
28
+ }
29
+
30
+ .button-primary:hover {
31
+ box-shadow: 0px -6px 12px 0px rgba(255, 0, 0, 0.2) inset,
32
+ 0px 4px 8px 0px rgba(255, 77, 0, 0.16),
33
+ 0px 1px 1px 0px rgba(255, 77, 0, 0.12),
34
+ 0px 0.5px 0.5px 0px rgba(255, 77, 0, 0.16),
35
+ 0px 0.25px 0.25px 0px rgba(255, 77, 0, 0.2);
36
+ box-shadow: 0px -6px 12px 0px color(display-p3 0.9804 0.1127 0.098 / 0.2) inset,
37
+ 0px 4px 8px 0px color(display-p3 0.9804 0.3647 0.098 / 0.16),
38
+ 0px 1px 1px 0px color(display-p3 0.9804 0.3647 0.098 / 0.12),
39
+ 0px 0.5px 0.5px 0px color(display-p3 0.9804 0.3647 0.098 / 0.16),
40
+ 0px 0.25px 0.25px 0px color(display-p3 0.9804 0.3647 0.098 / 0.2);
41
+ }
42
+
43
+ .button-primary:active {
44
+ box-shadow: 0px -6px 12px 0px rgba(255, 0, 0, 0.2) inset,
45
+ 0px 2px 4px 0px rgba(255, 77, 0, 0.12),
46
+ 0px 1px 1px 0px rgba(255, 77, 0, 0.12),
47
+ 0px 0.5px 0.5px 0px rgba(255, 77, 0, 0.16),
48
+ 0px 0.25px 0.25px 0px rgba(255, 77, 0, 0.2);
49
+ box-shadow: 0px -6px 12px 0px color(display-p3 0.9804 0.1127 0.098 / 0.2) inset,
50
+ 0px 2px 4px 0px color(display-p3 0.9804 0.3647 0.098 / 0.12),
51
+ 0px 1px 1px 0px color(display-p3 0.9804 0.3647 0.098 / 0.12),
52
+ 0px 0.5px 0.5px 0px color(display-p3 0.9804 0.3647 0.098 / 0.16),
53
+ 0px 0.25px 0.25px 0px color(display-p3 0.9804 0.3647 0.098 / 0.2);
54
+ }
55
+
56
+ .button-background {
57
+ background: linear-gradient(to bottom, white, transparent);
58
+
59
+ opacity: 0.06;
60
+
61
+ transition: opacity 0.2s cubic-bezier(0.25, 0.1, 0.25, 1);
62
+ }
63
+
64
+ .button:hover .button-background {
65
+ opacity: 0.08;
66
+ }
67
+
68
+ .button:active .button-background {
69
+ opacity: 0;
70
+
71
+ transition: opacity 0.05s cubic-bezier(0.25, 0.1, 0.25, 1);
72
+ }
styles/components/code.css ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .string,
2
+ .language-html .tag:not(.punctuation, .attr-name, .attr-value, .special-attr) {
3
+ color: var(--heat-100) !important;
4
+ }
5
+
6
+ .punctuation,
7
+ .operator {
8
+ color: #c2c2c2 !important;
9
+ }
10
+
11
+ .language-html .attr-name {
12
+ color: var(--black-alpha-64);
13
+ }
14
+
15
+ .comment {
16
+ color: #999999 !important;
17
+ }
18
+
19
+ code:not(.language-html) .property:not(.literal-property),
20
+ .class-name,
21
+ code:not(.language-html) .function,
22
+ .language-json .boolean {
23
+ color: #9061ff;
24
+ color: color(display-p3 0.566 0.38 1);
25
+ }
26
+
27
+ .language-json .property {
28
+ color: inherit !important;
29
+ }
30
+
31
+ .prismjs {
32
+ padding-top: 20px;
33
+ @apply text-mono-medium font-mono;
34
+ }
35
+
36
+ .prismjs code {
37
+ color: var(--accent-black) !important;
38
+ }
39
+
40
+ .linenumber {
41
+ padding: 0;
42
+ color:white!important;
43
+ font-style: normal;
44
+ }
styles/components/index.css ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Component-specific styles from firecrawl-marketing */
2
+
3
+ /* UI Components (for Dashboard v2) */
4
+ @import "./button.css";
5
+ @import "./code.css";
6
+
7
+ /* Additional component CSS will be added as we migrate more components */
8
+ /* @import "./modal.css"; */
9
+ /* @import "./spinner.css"; */
10
+ /* @import "./input.css"; */
styles/design-system/.cursor/rules/design-system.md ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Firecrawl Design System Rules
2
+
3
+ When working with the fire-inspired design system:
4
+
5
+ ## Important Note
6
+ - For existing pages: Continue using `styles/main.css`
7
+ - For Dashboard v2 pages: Import `styles/dashboard.css` in the layout
8
+
9
+ ## Color System
10
+ Use CSS custom properties with P3 color space for richer colors:
11
+ - **Heat colors**: `--heat-4` through `--heat-100` (fire orange shades)
12
+ - **Accent colors**: `--accent-black`, `--accent-amethyst`, `--accent-bluetron`, `--accent-crimson`
13
+ - **Alpha variants**: Black and white with various opacity levels
14
+ - **UI colors**: Borders, backgrounds, illustrations
15
+ - All colors have sRGB fallbacks for browser compatibility
16
+
17
+ ## Typography
18
+ **Font Families:**
19
+ - Display: SuisseIntl (weights: 400, 450, 500, 600, 700)
20
+ - Mono: System monospace stack (SF Mono, Monaco, Inconsolata, etc.)
21
+
22
+ **Type Scale Classes:**
23
+ - Headings: `.title-h1` through `.title-h5`
24
+ - Body: `.body-small`, `.body-medium`, `.body-large`, `.body-x-large`
25
+ - Labels: `.label-small`, `.label-medium`, `.label-large`, `.label-x-large`
26
+ - Mono: `.mono-small`, `.mono-medium`, `.mono-large`
27
+
28
+ ## Utilities
29
+ **Gradients:**
30
+ - `.gradient-fire` - Heat gradient
31
+ - `.gradient-heat` - Subtle heat gradient
32
+ - `.gradient-sunset` - Heat to amethyst
33
+ - `.gradient-ocean` - Blue gradient
34
+
35
+ **Layout:**
36
+ - `.container-prose` - Max 65ch width
37
+ - `.center-absolute` - Absolute centering
38
+ - `.stack-*` - Vertical spacing
39
+ - `.layout-sidebar` - Sidebar layout pattern
40
+
41
+ **Effects:**
42
+ - `.blur-backdrop` - Backdrop blur
43
+ - `.border-gradient` - Gradient border
44
+ - `.mask-fade-*` - Fade masks
45
+ - `.focus-ring` - Accessible focus states
46
+ - `.dotted-underline` - SVG dotted underline
47
+
48
+ ## Animation Classes
49
+ - `.cursor` - Blinking cursor
50
+ - `.animate-spin-reverse` - Reverse rotation
51
+ - `.animate-flicker` - Fire flicker effect
52
+ - `.animate-glow` - Glowing effect
53
+ - `.transition-*` - Smooth transitions
54
+
55
+ ## Usage Example
56
+ ```css
57
+ .fire-button {
58
+ background: var(--heat-100);
59
+ color: var(--white);
60
+ padding: 0.75rem 1.5rem;
61
+ border-radius: 0.5rem;
62
+ transition: all 0.3s ease;
63
+ }
64
+
65
+ .fire-button:hover {
66
+ background: var(--accent-crimson);
67
+ box-shadow: 0 0 40px rgba(250, 93, 25, 0.8);
68
+ }
69
+ ```
70
+
71
+ ## Component CSS Architecture
72
+ Component-specific styles go in `styles/components/`:
73
+ - Button.css - Fire-inspired button styles
74
+ - Input.css - Input with heat focus states
75
+ - Modal.css - Modal with fire animations
76
+
77
+ ## Build Optimization
78
+ - PostCSS handles optimization
79
+ - Tailwind v3 purging via content paths in tailwind.config.js
80
+ - P3 color space with automatic sRGB fallbacks
81
+ - Font preloading for performance
styles/design-system/animations.css ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Animation Utilities */
2
+
3
+ /* Cursor blink animation */
4
+ .cursor {
5
+ animation: cursor-blink 0.7s infinite;
6
+ }
7
+
8
+ @keyframes cursor-blink {
9
+ 0%, 100% {
10
+ opacity: 0;
11
+ }
12
+ 50% {
13
+ opacity: 1;
14
+ }
15
+ }
16
+
17
+ /* Reverse spin */
18
+ .animate-spin-reverse {
19
+ animation: spin-reverse 1s linear infinite;
20
+ }
21
+
22
+ @keyframes spin-reverse {
23
+ from {
24
+ transform: rotate(360deg);
25
+ }
26
+ }
27
+
28
+ /* Fire-inspired animations */
29
+ .animate-flicker {
30
+ animation: flicker 2s ease-in-out infinite;
31
+ }
32
+
33
+ @keyframes flicker {
34
+ 0%, 100% {
35
+ opacity: 1;
36
+ }
37
+ 50% {
38
+ opacity: 0.8;
39
+ transform: scale(0.98);
40
+ }
41
+ }
42
+
43
+ .animate-glow {
44
+ animation: glow 2s ease-in-out infinite;
45
+ }
46
+
47
+ @keyframes glow {
48
+ 0%, 100% {
49
+ box-shadow: 0 0 20px rgba(250, 93, 25, 0.5);
50
+ }
51
+ 50% {
52
+ box-shadow: 0 0 40px rgba(250, 93, 25, 0.8);
53
+ }
54
+ }
55
+
56
+ /* Smooth transitions */
57
+ .transition-all {
58
+ transition: all 0.3s ease;
59
+ }
60
+
61
+ .transition-colors {
62
+ transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease;
63
+ }
64
+
65
+ .transition-transform {
66
+ transition: transform 0.3s ease;
67
+ }
68
+
69
+ .transition-opacity {
70
+ transition: opacity 0.3s ease;
71
+ }
72
+
73
+ /* Animation delays */
74
+ .delay-100 {
75
+ animation-delay: 100ms;
76
+ }
77
+
78
+ .delay-200 {
79
+ animation-delay: 200ms;
80
+ }
81
+
82
+ .delay-300 {
83
+ animation-delay: 300ms;
84
+ }
85
+
86
+ .delay-400 {
87
+ animation-delay: 400ms;
88
+ }
89
+
90
+ .delay-500 {
91
+ animation-delay: 500ms;
92
+ }
styles/design-system/base/body.css ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Body and Global Styles */
2
+
3
+ /* Hide scrollbars but keep functionality */
4
+ ::-webkit-scrollbar {
5
+ width: 0;
6
+ height: 0;
7
+ }
8
+
9
+ ::-webkit-scrollbar-thumb:hover,
10
+ ::-webkit-scrollbar-thumb:active {
11
+ background-color: var(--black-alpha-12);
12
+ }
13
+
14
+ /* Global text rendering */
15
+ * {
16
+ -webkit-font-smoothing: antialiased;
17
+ -moz-osx-font-smoothing: grayscale;
18
+ text-rendering: optimizeLegibility;
19
+ }
20
+
21
+ /* Body defaults */
22
+ body {
23
+ font-family: var(--font-sans);
24
+ font-size: 1rem;
25
+ line-height: 1.5;
26
+ color: var(--accent-black);
27
+ background-color: var(--background-base);
28
+ overflow-anchor: none;
29
+ }
30
+
31
+ /* Prevent image dragging */
32
+ img,
33
+ video {
34
+ max-width: unset;
35
+ user-select: none;
36
+ -webkit-user-drag: none;
37
+ -khtml-user-drag: none;
38
+ -moz-user-drag: none;
39
+ -o-user-drag: none;
40
+ user-drag: none;
41
+ }
42
+
43
+ /* Form elements */
44
+ input,
45
+ input:focus,
46
+ textarea,
47
+ textarea:focus,
48
+ select,
49
+ select:focus {
50
+ outline: 0;
51
+ }
52
+
53
+ /* Focus states handled by utilities */
54
+ *:focus {
55
+ outline: 0;
56
+ }
57
+
58
+ /* Smooth scrolling */
59
+ html {
60
+ scroll-behavior: smooth;
61
+ }
62
+
63
+ @media (prefers-reduced-motion: reduce) {
64
+ html {
65
+ scroll-behavior: auto;
66
+ }
67
+ }
68
+
69
+ /* Section defaults */
70
+ section {
71
+ position: relative;
72
+ }
73
+
74
+ /* Text selection */
75
+ h1, h2, h3, h4, h5, h6, p {
76
+ user-select: text;
77
+ }
78
+
79
+ /* Button defaults */
80
+ button {
81
+ text-align: left;
82
+ user-select: none;
83
+ }
84
+
85
+ /* Selection color using design system colors */
86
+ *::selection {
87
+ background-color: var(--heat-20);
88
+ color: var(--accent-black);
89
+ }
90
+
91
+ /* Links */
92
+ a {
93
+ color: inherit;
94
+ text-decoration: none;
95
+ transition: color 0.2s ease;
96
+ }
97
+
98
+ a:hover {
99
+ color: var(--heat-100);
100
+ }
101
+
102
+ /* Code blocks */
103
+ code {
104
+ font-family: var(--font-mono);
105
+ font-size: 0.875em;
106
+ background-color: var(--black-alpha-4);
107
+ padding: 0.125rem 0.25rem;
108
+ border-radius: 0.25rem;
109
+ }
110
+
111
+ pre code {
112
+ background-color: transparent;
113
+ padding: 0;
114
+ }
115
+
116
+ /* Prevent FOUC */
117
+ html {
118
+ visibility: visible !important;
119
+ }
120
+
121
+ /* Responsive text sizing */
122
+ @media (max-width: 768px) {
123
+ html {
124
+ font-size: 15px;
125
+ }
126
+ }
127
+
128
+ @media (min-width: 1536px) {
129
+ html {
130
+ font-size: 17px;
131
+ }
132
+ }
styles/design-system/base/layout.css ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Layout Utilities and Helpers */
2
+
3
+ /* Container system */
4
+ .container {
5
+ width: 100%;
6
+ margin-inline: auto;
7
+ padding-inline: var(--container-padding, 1rem);
8
+ }
9
+
10
+ @media (min-width: 640px) {
11
+ .container {
12
+ max-width: 640px;
13
+ padding-inline: 1.5rem;
14
+ }
15
+ }
16
+
17
+ @media (min-width: 768px) {
18
+ .container {
19
+ max-width: 768px;
20
+ padding-inline: 2rem;
21
+ }
22
+ }
23
+
24
+ @media (min-width: 1024px) {
25
+ .container {
26
+ max-width: 1024px;
27
+ }
28
+ }
29
+
30
+ @media (min-width: 1280px) {
31
+ .container {
32
+ max-width: 1280px;
33
+ }
34
+ }
35
+
36
+ @media (min-width: 1536px) {
37
+ .container {
38
+ max-width: 1536px;
39
+ }
40
+ }
41
+
42
+ /* Layout patterns */
43
+ .layout-grid {
44
+ display: grid;
45
+ gap: var(--gap, 1rem);
46
+ }
47
+
48
+ .layout-flex {
49
+ display: flex;
50
+ gap: var(--gap, 1rem);
51
+ }
52
+
53
+ /* Common grid patterns */
54
+ .grid-cols-auto-fit {
55
+ grid-template-columns: repeat(auto-fit, minmax(var(--min-width, 250px), 1fr));
56
+ }
57
+
58
+ .grid-cols-auto-fill {
59
+ grid-template-columns: repeat(auto-fill, minmax(var(--min-width, 250px), 1fr));
60
+ }
61
+
62
+ /* Sidebar layouts */
63
+ .layout-sidebar {
64
+ display: grid;
65
+ grid-template-columns: var(--sidebar-width, 260px) 1fr;
66
+ gap: var(--gap, 2rem);
67
+ align-items: start;
68
+ }
69
+
70
+ @media (max-width: 1024px) {
71
+ .layout-sidebar {
72
+ grid-template-columns: 1fr;
73
+ }
74
+ }
75
+
76
+ /* Stack layouts */
77
+ .stack {
78
+ display: flex;
79
+ flex-direction: column;
80
+ gap: var(--gap, 1rem);
81
+ }
82
+
83
+ .stack-horizontal {
84
+ display: flex;
85
+ flex-direction: row;
86
+ align-items: center;
87
+ gap: var(--gap, 1rem);
88
+ }
89
+
90
+ /* Cluster layout */
91
+ .cluster {
92
+ display: flex;
93
+ flex-wrap: wrap;
94
+ gap: var(--gap, 1rem);
95
+ align-items: center;
96
+ }
97
+
98
+ /* Center layout */
99
+ .center {
100
+ display: grid;
101
+ place-items: center;
102
+ min-height: var(--min-height, 100vh);
103
+ }
104
+
105
+ /* Cover layout */
106
+ .cover {
107
+ display: flex;
108
+ flex-direction: column;
109
+ min-height: var(--min-height, 100vh);
110
+ }
111
+
112
+ .cover > * {
113
+ margin-block: 1rem;
114
+ }
115
+
116
+ .cover > :first-child:not(.centered) {
117
+ margin-block-start: 0;
118
+ }
119
+
120
+ .cover > :last-child:not(.centered) {
121
+ margin-block-end: 0;
122
+ }
123
+
124
+ .cover > .centered {
125
+ margin-block: auto;
126
+ }
127
+
128
+ /* Switcher layout (switches from horizontal to vertical) */
129
+ .switcher {
130
+ display: flex;
131
+ flex-wrap: wrap;
132
+ gap: var(--gap, 1rem);
133
+ }
134
+
135
+ .switcher > * {
136
+ flex-grow: 1;
137
+ flex-basis: calc((var(--threshold, 30rem) - 100%) * 999);
138
+ }
139
+
140
+ /* Frame layout (for aspect ratios) */
141
+ .frame {
142
+ aspect-ratio: var(--ratio, 16 / 9);
143
+ overflow: hidden;
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ }
148
+
149
+ .frame > img,
150
+ .frame > video {
151
+ width: 100%;
152
+ height: 100%;
153
+ object-fit: cover;
154
+ }
155
+
156
+ /* Reel layout (horizontal scroll) */
157
+ .reel {
158
+ display: flex;
159
+ gap: var(--gap, 1rem);
160
+ overflow-x: auto;
161
+ overflow-y: hidden;
162
+ scrollbar-width: none;
163
+ -webkit-overflow-scrolling: touch;
164
+ }
165
+
166
+ .reel::-webkit-scrollbar {
167
+ display: none;
168
+ }
169
+
170
+ .reel > * {
171
+ flex: 0 0 auto;
172
+ }
173
+
174
+ /* Full bleed */
175
+ .full-bleed {
176
+ width: 100vw;
177
+ margin-inline: calc(50% - 50vw);
178
+ }
179
+
180
+ /* Visually hidden but accessible */
181
+ .sr-only {
182
+ position: absolute;
183
+ width: 1px;
184
+ height: 1px;
185
+ padding: 0;
186
+ margin: -1px;
187
+ overflow: hidden;
188
+ clip: rect(0, 0, 0, 0);
189
+ white-space: nowrap;
190
+ border-width: 0;
191
+ }
192
+
193
+ /* Skip to content link */
194
+ .skip-link {
195
+ position: absolute;
196
+ top: 1rem;
197
+ left: 1rem;
198
+ z-index: 999;
199
+ padding: 0.5rem 1rem;
200
+ background-color: var(--accent-black);
201
+ color: var(--white);
202
+ text-decoration: none;
203
+ border-radius: 0.25rem;
204
+ opacity: 0;
205
+ transform: translateY(-100%);
206
+ transition: opacity 0.2s, transform 0.2s;
207
+ }
208
+
209
+ .skip-link:focus {
210
+ opacity: 1;
211
+ transform: translateY(0);
212
+ }
styles/design-system/base/reset.css ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Modern CSS Reset */
2
+
3
+ /* Box sizing rules */
4
+ *,
5
+ *::before,
6
+ *::after {
7
+ box-sizing: border-box;
8
+ }
9
+
10
+ /* Remove default margin */
11
+ body,
12
+ h1,
13
+ h2,
14
+ h3,
15
+ h4,
16
+ h5,
17
+ h6,
18
+ p,
19
+ figure,
20
+ blockquote,
21
+ dl,
22
+ dd {
23
+ margin: 0;
24
+ }
25
+
26
+ /* Remove list styles on ul, ol elements with a list role */
27
+ ul[role='list'],
28
+ ol[role='list'] {
29
+ list-style: none;
30
+ padding: 0;
31
+ margin: 0;
32
+ }
33
+
34
+ /* Set core root defaults */
35
+ html:focus-within {
36
+ scroll-behavior: smooth;
37
+ }
38
+
39
+ /* Set core body defaults */
40
+ body {
41
+ min-height: 100vh;
42
+ text-rendering: optimizeSpeed;
43
+ line-height: 1.5;
44
+ -webkit-font-smoothing: antialiased;
45
+ -moz-osx-font-smoothing: grayscale;
46
+ }
47
+
48
+ /* A elements that don't have a class get default styles */
49
+ a:not([class]) {
50
+ text-decoration-skip-ink: auto;
51
+ }
52
+
53
+ /* Make images easier to work with */
54
+ img,
55
+ picture,
56
+ video,
57
+ canvas,
58
+ svg {
59
+ display: block;
60
+ max-width: 100%;
61
+ }
62
+
63
+ /* Inherit fonts for inputs and buttons */
64
+ input,
65
+ button,
66
+ textarea,
67
+ select {
68
+ font: inherit;
69
+ }
70
+
71
+ /* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
72
+ @media (prefers-reduced-motion: reduce) {
73
+ html:focus-within {
74
+ scroll-behavior: auto;
75
+ }
76
+
77
+ *,
78
+ *::before,
79
+ *::after {
80
+ animation-duration: 0.01ms !important;
81
+ animation-iteration-count: 1 !important;
82
+ transition-duration: 0.01ms !important;
83
+ scroll-behavior: auto !important;
84
+ }
85
+ }
86
+
87
+ /* Reset button styles */
88
+ button {
89
+ background: none;
90
+ border: none;
91
+ padding: 0;
92
+ cursor: pointer;
93
+ text-align: inherit;
94
+ color: inherit;
95
+ }
96
+
97
+ /* Form elements */
98
+ fieldset {
99
+ margin: 0;
100
+ padding: 0;
101
+ border: 0;
102
+ }
103
+
104
+ /* Text area */
105
+ textarea {
106
+ resize: vertical;
107
+ }
108
+
109
+ /* Remove default focus styles and add custom ones */
110
+ :focus {
111
+ outline: none;
112
+ }
113
+
114
+ :focus-visible {
115
+ outline: 2px solid var(--heat-100);
116
+ outline-offset: 2px;
117
+ }
118
+
119
+ /* Prevent iOS zoom on form focus */
120
+ @media (hover: none) {
121
+ input[type="text"],
122
+ input[type="email"],
123
+ input[type="password"],
124
+ input[type="number"],
125
+ input[type="tel"],
126
+ input[type="search"],
127
+ input[type="url"],
128
+ textarea,
129
+ select {
130
+ font-size: 16px !important;
131
+ }
132
+ }
styles/design-system/colors.css ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Fire Design System Colors */
2
+ /* Define sRGB values first for maximum compatibility */
3
+ :root {
4
+ /* Base colors */
5
+ --white: #ffffff;
6
+ --black: #000000;
7
+
8
+ /* Fire-inspired heat colors */
9
+ --heat-4: rgba(250, 93, 25, 0.039);
10
+ --heat-8: rgba(250, 93, 25, 0.078);
11
+ --heat-12: rgba(250, 93, 25, 0.122);
12
+ --heat-16: rgba(250, 93, 25, 0.161);
13
+ --heat-20: rgba(250, 93, 25, 0.200);
14
+ --heat-40: rgba(250, 93, 25, 0.400);
15
+ --heat-100: #fa5d19;
16
+ --heat-200: #ff6600;
17
+
18
+ /* Accent colors */
19
+ --accent-black: #262626;
20
+ --accent-white: #ffffff;
21
+ --accent-amethyst: #9061ff;
22
+ --accent-bluetron: #2a6dfb;
23
+ --accent-crimson: #eb3424;
24
+
25
+ /* Black alpha variants */
26
+ --black-alpha-1: rgba(0, 0, 0, 0.012);
27
+ --black-alpha-2: rgba(0, 0, 0, 0.020);
28
+ --black-alpha-3: rgba(0, 0, 0, 0.031);
29
+ --black-alpha-4: rgba(0, 0, 0, 0.039);
30
+ --black-alpha-5: rgba(0, 0, 0, 0.051);
31
+ --black-alpha-6: rgba(0, 0, 0, 0.059);
32
+ --black-alpha-7: rgba(0, 0, 0, 0.071);
33
+ --black-alpha-8: rgba(0, 0, 0, 0.078);
34
+ --black-alpha-10: rgba(0, 0, 0, 0.102);
35
+ --black-alpha-12: rgba(0, 0, 0, 0.122);
36
+ --black-alpha-16: rgba(0, 0, 0, 0.161);
37
+ --black-alpha-20: rgba(0, 0, 0, 0.200);
38
+ --black-alpha-24: rgba(0, 0, 0, 0.239);
39
+ --black-alpha-32: rgba(38, 38, 38, 0.322);
40
+ --black-alpha-40: rgba(38, 38, 38, 0.400);
41
+ --black-alpha-48: rgba(38, 38, 38, 0.478);
42
+ --black-alpha-56: rgba(38, 38, 38, 0.561);
43
+ --black-alpha-64: rgba(38, 38, 38, 0.639);
44
+ --black-alpha-72: rgba(38, 38, 38, 0.722);
45
+ --black-alpha-88: rgba(38, 38, 38, 0.878);
46
+
47
+ /* White alpha variants */
48
+ --white-alpha-56: rgba(255, 255, 255, 0.561);
49
+ --white-alpha-72: rgba(255, 255, 255, 0.722);
50
+
51
+ /* Border colors */
52
+ --border-faint: #ededed;
53
+ --border-muted: #e8e8e8;
54
+ --border-loud: #e6e6e6;
55
+
56
+ /* Illustration colors */
57
+ --illustrations-faint: #ededed;
58
+ --illustrations-muted: #e6e6e6;
59
+ --illustrations-default: #dbdbdb;
60
+
61
+ /* Background colors */
62
+ --background-lighter: #fbfbfb;
63
+ --background-base: #f9f9f9;
64
+
65
+ /* Foreground colors */
66
+ --foreground: #262626;
67
+ --foreground-dimmer: rgba(38, 38, 38, 0.722);
68
+ }
69
+
70
+ /* P3 color space enhancement for supported browsers */
71
+ @supports (color: color(display-p3 1 1 1)) {
72
+ :root {
73
+ /* Base colors */
74
+ --white: color(display-p3 1 1 1);
75
+ --black: color(display-p3 0 0 0);
76
+
77
+ /* Fire-inspired heat colors */
78
+ --heat-4: color(display-p3 0.980392 0.364706 0.098039 / 0.039216);
79
+ --heat-8: color(display-p3 0.980392 0.364706 0.098039 / 0.078431);
80
+ --heat-12: color(display-p3 0.980392 0.364706 0.098039 / 0.121569);
81
+ --heat-16: color(display-p3 0.980392 0.364706 0.098039 / 0.160784);
82
+ --heat-20: color(display-p3 0.980392 0.364706 0.098039 / 0.200000);
83
+ --heat-40: color(display-p3 0.980392 0.364706 0.098039 / 0.400000);
84
+ --heat-100: color(display-p3 0.980392 0.364706 0.098039 / 1.000000);
85
+ --heat-200: color(display-p3 1.000000 0.400000 0.000000 / 1.000000);
86
+
87
+ /* Accent colors */
88
+ --accent-black: color(display-p3 0.149020 0.149020 0.149020 / 1.000000);
89
+ --accent-white: color(display-p3 1.000000 1.000000 1.000000 / 1.000000);
90
+ --accent-amethyst: color(display-p3 0.564706 0.380392 1.000000 / 1.000000);
91
+ --accent-bluetron: color(display-p3 0.164706 0.427451 0.984314 / 1.000000);
92
+ --accent-crimson: color(display-p3 0.921569 0.203922 0.141176 / 1.000000);
93
+
94
+ /* Black alpha variants */
95
+ --black-alpha-1: color(display-p3 0.000000 0.000000 0.000000 / 0.011765);
96
+ --black-alpha-2: color(display-p3 0.000000 0.000000 0.000000 / 0.019608);
97
+ --black-alpha-3: color(display-p3 0.000000 0.000000 0.000000 / 0.031373);
98
+ --black-alpha-4: color(display-p3 0.000000 0.000000 0.000000 / 0.039216);
99
+ --black-alpha-5: color(display-p3 0.000000 0.000000 0.000000 / 0.050980);
100
+ --black-alpha-6: color(display-p3 0.000000 0.000000 0.000000 / 0.058824);
101
+ --black-alpha-7: color(display-p3 0.000000 0.000000 0.000000 / 0.070588);
102
+ --black-alpha-8: color(display-p3 0.000000 0.000000 0.000000 / 0.078431);
103
+ --black-alpha-10: color(display-p3 0.000000 0.000000 0.000000 / 0.101961);
104
+ --black-alpha-12: color(display-p3 0.000000 0.000000 0.000000 / 0.121569);
105
+ --black-alpha-16: color(display-p3 0.000000 0.000000 0.000000 / 0.160784);
106
+ --black-alpha-20: color(display-p3 0.000000 0.000000 0.000000 / 0.200000);
107
+ --black-alpha-24: color(display-p3 0.000000 0.000000 0.000000 / 0.239216);
108
+ --black-alpha-32: color(display-p3 0.149020 0.149020 0.149020 / 0.321569);
109
+ --black-alpha-40: color(display-p3 0.149020 0.149020 0.149020 / 0.400000);
110
+ --black-alpha-48: color(display-p3 0.149020 0.149020 0.149020 / 0.478431);
111
+ --black-alpha-56: color(display-p3 0.149020 0.149020 0.149020 / 0.560784);
112
+ --black-alpha-64: color(display-p3 0.149020 0.149020 0.149020 / 0.639216);
113
+ --black-alpha-72: color(display-p3 0.149020 0.149020 0.149020 / 0.721569);
114
+ --black-alpha-88: color(display-p3 0.149020 0.149020 0.149020 / 0.878431);
115
+
116
+ /* White alpha variants */
117
+ --white-alpha-56: color(display-p3 1.000000 1.000000 1.000000 / 0.560784);
118
+ --white-alpha-72: color(display-p3 1.000000 1.000000 1.000000 / 0.721569);
119
+
120
+ /* Border colors */
121
+ --border-faint: color(display-p3 0.929412 0.929412 0.929412 / 1.000000);
122
+ --border-muted: color(display-p3 0.909804 0.909804 0.909804 / 1.000000);
123
+ --border-loud: color(display-p3 0.901961 0.901961 0.901961 / 1.000000);
124
+
125
+ /* Illustration colors */
126
+ --illustrations-faint: color(display-p3 0.929412 0.929412 0.929412 / 1.000000);
127
+ --illustrations-muted: color(display-p3 0.901961 0.901961 0.901961 / 1.000000);
128
+ --illustrations-default: color(display-p3 0.858824 0.858824 0.858824 / 1.000000);
129
+
130
+ /* Background colors */
131
+ --background-lighter: color(display-p3 0.984314 0.984314 0.984314 / 1.000000);
132
+ --background-base: color(display-p3 0.976471 0.976471 0.976471 / 1.000000);
133
+ }
134
+ }
135
+
styles/design-system/fonts.css ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ /* Custom Font Faces */
2
+ /* Using Geist fonts via CSS variables from Next.js font loading */
3
+ /* Fonts are loaded in app/layout.tsx and available as:
4
+ - var(--font-geist-sans)
5
+ - var(--font-geist-mono)
6
+ - var(--font-inter)
7
+ - var(--font-roboto-mono)
8
+ */
styles/design-system/typography.css ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Typography System */
2
+
3
+ /* Font Face Declarations - matching firecrawl-marketing exactly */
4
+ @font-face {
5
+ font-family: 'SuisseIntl';
6
+ src: url('/fonts/SuisseIntl/400.woff2') format('woff2');
7
+ font-weight: 400;
8
+ font-style: normal;
9
+ font-display: swap;
10
+ }
11
+
12
+ @font-face {
13
+ font-family: 'SuisseIntl';
14
+ src: url('/fonts/SuisseIntl/450.woff2') format('woff2');
15
+ font-weight: 450;
16
+ font-style: normal;
17
+ font-display: swap;
18
+ }
19
+
20
+ @font-face {
21
+ font-family: 'SuisseIntl';
22
+ src: url('/fonts/SuisseIntl/500.woff2') format('woff2');
23
+ font-weight: 500;
24
+ font-style: normal;
25
+ font-display: swap;
26
+ }
27
+
28
+ @font-face {
29
+ font-family: 'SuisseIntl';
30
+ src: url('/fonts/SuisseIntl/600.woff2') format('woff2');
31
+ font-weight: 600;
32
+ font-style: normal;
33
+ font-display: swap;
34
+ }
35
+
36
+ @font-face {
37
+ font-family: 'SuisseIntl';
38
+ src: url('/fonts/SuisseIntl/700.woff2') format('woff2');
39
+ font-weight: 700;
40
+ font-style: normal;
41
+ font-display: swap;
42
+ }
43
+
44
+ /* Font Stack Variables */
45
+ :root {
46
+ --font-sans: 'SuisseIntl', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
47
+ --font-mono: 'SF Mono', 'Monaco', 'Inconsolata', 'Fira Code', 'Roboto Mono', Consolas, monospace;
48
+ }
49
+
50
+ /* Typography Classes - both semantic and utility variants */
51
+
52
+ /* Text Utilities */
53
+ .text-balance {
54
+ text-wrap: balance;
55
+ }
56
+
57
+ .text-pretty {
58
+ text-wrap: pretty;
59
+ }
60
+
61
+ /* Utility text classes matching firecrawl-marketing - extending base classes */
62
+ .title-h1,
63
+ .text-title-h1 {
64
+ font-size: 4rem; /* 64px */
65
+ line-height: 1.1;
66
+ letter-spacing: -0.02em;
67
+ font-weight: 600;
68
+ }
69
+
70
+ .title-h2,
71
+ .text-title-h2 {
72
+ font-size: 2.25rem; /* 36px */
73
+ line-height: 1.2;
74
+ letter-spacing: -0.02em;
75
+ font-weight: 500;
76
+ }
77
+
78
+ .title-h3,
79
+ .text-title-h3 {
80
+ font-size: 1.875rem; /* 30px */
81
+ line-height: 1.25;
82
+ letter-spacing: -0.02em;
83
+ font-weight: 500;
84
+ }
85
+
86
+ .title-h4,
87
+ .text-title-h4 {
88
+ font-size: 1.375rem; /* 22px */
89
+ line-height: 1.3;
90
+ letter-spacing: -0.01em;
91
+ font-weight: 500;
92
+ }
93
+
94
+ .title-h5,
95
+ .text-title-h5 {
96
+ font-size: 1.125rem; /* 18px */
97
+ line-height: 1.4;
98
+ letter-spacing: -0.01em;
99
+ font-weight: 500;
100
+ }
101
+
102
+ .body-small,
103
+ .text-body-small {
104
+ font-size: 0.875rem; /* 14px */
105
+ line-height: 1.5;
106
+ font-weight: 400;
107
+ }
108
+
109
+ .body-medium,
110
+ .text-body-medium {
111
+ font-size: 1rem; /* 16px */
112
+ line-height: 1.5;
113
+ font-weight: 400;
114
+ }
115
+
116
+ .body-large,
117
+ .text-body-large {
118
+ font-size: 1.125rem; /* 18px */
119
+ line-height: 1.6;
120
+ font-weight: 400;
121
+ }
122
+
123
+ .body-x-large,
124
+ .text-body-x-large {
125
+ font-size: 1.375rem; /* 22px */
126
+ line-height: 1.6;
127
+ font-weight: 400;
128
+ }
129
+
130
+ .label-small,
131
+ .text-label-small {
132
+ font-size: 0.9375rem; /* 15px */
133
+ line-height: 1.4;
134
+ letter-spacing: 0.02em;
135
+ font-weight: 450;
136
+ }
137
+
138
+ .label-medium,
139
+ .text-label-medium {
140
+ font-size: 1rem; /* 16px */
141
+ line-height: 1.4;
142
+ letter-spacing: 0.01em;
143
+ font-weight: 450;
144
+ }
145
+
146
+ .label-large,
147
+ .text-label-large {
148
+ font-size: 1.125rem; /* 18px */
149
+ line-height: 1.4;
150
+ font-weight: 450;
151
+ }
152
+
153
+ .label-x-large,
154
+ .text-label-x-large {
155
+ font-size: 1.375rem; /* 22px */
156
+ line-height: 1.4;
157
+ font-weight: 450;
158
+ }
159
+
160
+ .mono-small,
161
+ .text-mono-small {
162
+ font-family: var(--font-mono);
163
+ font-size: 0.75rem; /* 12px */
164
+ line-height: 1.5;
165
+ }
166
+
167
+ .mono-medium,
168
+ .text-mono-medium {
169
+ font-family: var(--font-mono);
170
+ font-size: 0.875rem; /* 14px */
171
+ line-height: 1.5;
172
+ }
173
+
174
+ .mono-large,
175
+ .text-mono-large {
176
+ font-family: var(--font-mono);
177
+ font-size: 1rem; /* 16px */
178
+ line-height: 1.5;
179
+ }
180
+
181
+ /* Responsive Typography - matching firecrawl-marketing */
182
+ @media (max-width: 1024px) {
183
+ .text-body-large {
184
+ font-size: 18px;
185
+ font-weight: 400;
186
+ line-height: 26px;
187
+ letter-spacing: -0.09px;
188
+ }
189
+
190
+ .text-body-medium {
191
+ font-size: 16px;
192
+ font-weight: 400;
193
+ line-height: 24px;
194
+ letter-spacing: 0.16px;
195
+ }
196
+
197
+ .text-title-h4 {
198
+ font-size: 22px;
199
+ font-weight: 500;
200
+ line-height: 26px;
201
+ letter-spacing: -0.22px;
202
+ }
203
+
204
+ .text-body-x-large {
205
+ font-size: 22px;
206
+ font-weight: 400;
207
+ line-height: 30px;
208
+ letter-spacing: -0.11px;
209
+ }
210
+
211
+ .text-label-small {
212
+ font-size: 15px;
213
+ font-weight: 450;
214
+ line-height: 22px;
215
+ }
216
+
217
+ .text-label-large {
218
+ font-size: 18px;
219
+ font-weight: 450;
220
+ line-height: 26px;
221
+ letter-spacing: -0.09px;
222
+ }
223
+
224
+ .text-label-x-large {
225
+ font-size: 22px;
226
+ font-weight: 450;
227
+ line-height: 30px;
228
+ letter-spacing: -0.11px;
229
+ }
230
+
231
+ .text-label-medium {
232
+ font-size: 16px;
233
+ font-weight: 450;
234
+ line-height: 24px;
235
+ }
236
+
237
+ .text-title-h2 {
238
+ font-size: 36px;
239
+ font-weight: 500;
240
+ line-height: 40px;
241
+ letter-spacing: -0.36px;
242
+ }
243
+ }
styles/design-system/utilities.css ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Custom Utility Classes */
2
+
3
+ /* Gradient utilities */
4
+ .gradient-fire {
5
+ background: linear-gradient(135deg, var(--heat-100) 0%, var(--accent-crimson) 100%);
6
+ }
7
+
8
+ .gradient-heat {
9
+ background: linear-gradient(135deg, var(--heat-20) 0%, var(--heat-100) 100%);
10
+ }
11
+
12
+ .gradient-sunset {
13
+ background: linear-gradient(135deg, var(--heat-100) 0%, var(--accent-amethyst) 100%);
14
+ }
15
+
16
+ .gradient-ocean {
17
+ background: linear-gradient(135deg, var(--accent-bluetron) 0%, var(--accent-amethyst) 100%);
18
+ }
19
+
20
+ /* Text gradient */
21
+ .text-gradient {
22
+ background-clip: text;
23
+ -webkit-background-clip: text;
24
+ -webkit-text-fill-color: transparent;
25
+ background-color: var(--heat-100); /* Fallback */
26
+ }
27
+
28
+ /* Mask utilities */
29
+ .mask-fade-bottom {
30
+ mask-image: linear-gradient(to bottom, black 60%, transparent 100%);
31
+ -webkit-mask-image: linear-gradient(to bottom, black 60%, transparent 100%);
32
+ }
33
+
34
+ .mask-fade-edges {
35
+ mask-image: radial-gradient(ellipse at center, black 50%, transparent 100%);
36
+ -webkit-mask-image: radial-gradient(ellipse at center, black 50%, transparent 100%);
37
+ }
38
+
39
+ .mask-intersect {
40
+ -webkit-mask-composite: source-in; /* For Chrome */
41
+ mask-composite: intersect; /* For Firefox */
42
+ }
43
+
44
+ /* Blur utilities */
45
+ .blur-backdrop {
46
+ backdrop-filter: blur(10px);
47
+ -webkit-backdrop-filter: blur(10px);
48
+ }
49
+
50
+ /* Border utilities */
51
+ .border-gradient {
52
+ position: relative;
53
+ background: var(--white);
54
+ border: 1px solid transparent;
55
+ }
56
+
57
+ .border-gradient::before {
58
+ content: '';
59
+ position: absolute;
60
+ inset: 0;
61
+ border-radius: inherit;
62
+ padding: 1px;
63
+ background: linear-gradient(135deg, var(--heat-100), var(--accent-crimson));
64
+ mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
65
+ -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
66
+ mask-composite: xor;
67
+ -webkit-mask-composite: xor;
68
+ pointer-events: none;
69
+ }
70
+
71
+ /* Dotted underline - updated from marketing */
72
+ .dotted-underline {
73
+ position: relative;
74
+ display: inline-block;
75
+ line-height: 1.1;
76
+ }
77
+
78
+ .dotted-underline::before {
79
+ content: "";
80
+ position: absolute;
81
+ bottom: -2px;
82
+ left: 0;
83
+ width: 100%;
84
+ height: 1.7px;
85
+ background-image: url("data:image/svg+xml,%3Csvg width='4' height='2' viewBox='0 0 4 2' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='1.7' height='1.7' rx='0.85' fill='%23D1D1D1' style='fill:%23D1D1D1;fill:color(display-p3 0.8196 0.8196 0.8196);fill-opacity:1;'/%3E%3C/svg%3E%0A");
86
+ background-size: 3.4px 1.7px;
87
+ background-repeat: repeat-x;
88
+ }
89
+
90
+ /* Container utilities */
91
+ .container-prose {
92
+ max-width: 65ch;
93
+ margin-inline: auto;
94
+ }
95
+
96
+ .container-narrow {
97
+ max-width: 48rem; /* 768px */
98
+ margin-inline: auto;
99
+ }
100
+
101
+ .container-wide {
102
+ max-width: 80rem; /* 1280px */
103
+ margin-inline: auto;
104
+ }
105
+
106
+ /* Centering helpers */
107
+ .center-absolute {
108
+ position: absolute;
109
+ top: 50%;
110
+ left: 50%;
111
+ transform: translate(-50%, -50%);
112
+ }
113
+
114
+ .center-flex {
115
+ display: flex;
116
+ align-items: center;
117
+ justify-content: center;
118
+ }
119
+
120
+ /* Spacing utilities */
121
+ .stack-sm > * + * {
122
+ margin-top: 0.5rem;
123
+ }
124
+
125
+ .stack-md > * + * {
126
+ margin-top: 1rem;
127
+ }
128
+
129
+ .stack-lg > * + * {
130
+ margin-top: 2rem;
131
+ }
132
+
133
+ .stack-xl > * + * {
134
+ margin-top: 3rem;
135
+ }
136
+
137
+ /* Aspect ratio utilities */
138
+ .aspect-video {
139
+ aspect-ratio: 16 / 9;
140
+ }
141
+
142
+ .aspect-square {
143
+ aspect-ratio: 1 / 1;
144
+ }
145
+
146
+ .aspect-portrait {
147
+ aspect-ratio: 3 / 4;
148
+ }
149
+
150
+ /* Selection color */
151
+ ::selection {
152
+ background-color: var(--heat-20);
153
+ color: var(--accent-black);
154
+ }
155
+
156
+ /* Focus visible utilities */
157
+ .focus-ring {
158
+ outline: 2px solid transparent;
159
+ outline-offset: 2px;
160
+ }
161
+
162
+ .focus-ring:focus-visible {
163
+ outline-color: var(--heat-100);
164
+ }
165
+
166
+ /* Scrollbar styling */
167
+ .scrollbar-thin {
168
+ scrollbar-width: thin;
169
+ scrollbar-color: var(--border-muted) var(--background-lighter);
170
+ }
171
+
172
+ .scrollbar-thin::-webkit-scrollbar {
173
+ width: 8px;
174
+ height: 8px;
175
+ }
176
+
177
+ .scrollbar-thin::-webkit-scrollbar-track {
178
+ background: var(--background-lighter);
179
+ }
180
+
181
+ .scrollbar-thin::-webkit-scrollbar-thumb {
182
+ background-color: var(--border-muted);
183
+ border-radius: 4px;
184
+ }
185
+
186
+ .scrollbar-thin::-webkit-scrollbar-thumb:hover {
187
+ background-color: var(--border-loud);
188
+ }
189
+
190
+ /* Animation delay utilities */
191
+ .animation-delay-150 {
192
+ animation-delay: 150ms;
193
+ }
194
+
195
+ .animation-delay-300 {
196
+ animation-delay: 300ms;
197
+ }
198
+
199
+ .animation-delay-450 {
200
+ animation-delay: 450ms;
201
+ }
202
+
203
+ .animation-delay-600 {
204
+ animation-delay: 600ms;
205
+ }
206
+
207
+ /* Loading animation helpers */
208
+ .animate-shimmer {
209
+ background-size: 400% 100%;
210
+ animation: shimmer 2s ease-in-out infinite;
211
+ }
212
+
213
+ @keyframes shimmer {
214
+ 0% {
215
+ background-position: -200% 0;
216
+ }
217
+ 100% {
218
+ background-position: 200% 0;
219
+ }
220
+ }
221
+
222
+ /* Heat glow effect */
223
+ .heat-glow {
224
+ box-shadow: 0 0 40px rgba(250, 93, 25, 0.3);
225
+ animation: heat-glow 3s ease-in-out infinite;
226
+ }
227
+
228
+ @keyframes heat-glow {
229
+ 0%, 100% {
230
+ box-shadow: 0 0 20px rgba(250, 93, 25, 0.2);
231
+ }
232
+ 50% {
233
+ box-shadow: 0 0 40px rgba(250, 93, 25, 0.4);
234
+ }
235
+ }
styles/fire.css ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Fire Design System - New Firecrawl Design System */
2
+ /* This is the new fire-inspired design system used in app and brand pages */
3
+
4
+ /* ALL @import statements must come FIRST before any other CSS */
5
+
6
+ /* Fire Design System Base */
7
+ @import "./design-system/base/reset.css";
8
+ @import "./design-system/base/body.css";
9
+ @import "./design-system/base/layout.css";
10
+
11
+ /* Fire Design System Core */
12
+ @import "./design-system/fonts.css";
13
+ @import "./design-system/colors.css";
14
+ @import "./design-system/typography.css";
15
+ @import "./design-system/animations.css";
16
+ @import "./design-system/utilities.css";
17
+
18
+ /* Tailwind CSS - After all imports */
19
+ @tailwind base;
20
+ @tailwind components;
21
+ @tailwind utilities;
22
+
23
+ /* Container configuration from firecrawl-marketing */
24
+ :root {
25
+ --container-width: 1112px;
26
+ --container-width-padding: 1144px;
27
+ }
28
+
29
+ /* Container implementation matching firecrawl-marketing */
30
+ .container {
31
+ max-width: min(calc(100% - 32px), var(--container-width));
32
+ --padding: 16px;
33
+ margin-left: auto;
34
+ margin-right: auto;
35
+ }
36
+
37
+ /* Custom container width utilities */
38
+ .cmw-container {
39
+ max-width: var(--container-width);
40
+ padding-left: 16px;
41
+ padding-right: 16px;
42
+ margin-left: auto;
43
+ margin-right: auto;
44
+ }
45
+
46
+ @media (max-width: 1024px) {
47
+ :root {
48
+ --container-width: 356px;
49
+ --container-width-padding: var(--container-width);
50
+ }
51
+
52
+ .container {
53
+ --padding: 0px;
54
+ }
55
+ }
56
+
57
+ /* Marketing pattern utilities */
58
+ .overlay {
59
+ position: absolute;
60
+ top: 0;
61
+ left: 0;
62
+ width: 100%;
63
+ height: 100%;
64
+ border-radius: inherit;
65
+ }
66
+
67
+ .curvy-rect {
68
+ contain: layout paint;
69
+ }
70
+
71
+ /* Fire-specific utilities layer */
72
+ @layer utilities {
73
+ /* Mask utilities */
74
+ .mask-intersect {
75
+ -webkit-mask-composite: source-in; /* For Chrome */
76
+ mask-composite: intersect; /* Standard */
77
+ }
78
+
79
+ .mask-subtract {
80
+ -webkit-mask-composite: source-out; /* For Chrome */
81
+ mask-composite: subtract; /* Standard */
82
+ }
83
+
84
+ .mask-union {
85
+ -webkit-mask-composite: source-over; /* For Chrome */
86
+ mask-composite: add; /* Standard */
87
+ }
88
+
89
+ /* Fire design pattern utilities */
90
+ .dotted-underline::before {
91
+ content: "";
92
+ position: absolute;
93
+ bottom: -3px;
94
+ left: 0;
95
+ width: 100%;
96
+ height: 1.7px;
97
+ background-image: url("data:image/svg+xml,%3Csvg width='4' height='2' viewBox='0 0 4 2' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='1.7' height='1.7' rx='0.85' fill='%23D1D1D1' style='fill:%23D1D1D1;fill:color(display-p3 0.8196 0.8196 0.8196);fill-opacity:1;'/%3E%3C/svg%3E%0A");
98
+ background-size: 3.4px 1.7px;
99
+ background-repeat: repeat-x;
100
+ }
101
+
102
+ /* inside-border styles moved to inside-border-fix.css to avoid conflicts */
103
+ }
styles/inside-border-fix.css ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Fix for inside-border utility */
2
+ .inside-border {
3
+ position: relative;
4
+ }
5
+
6
+ .inside-border::before {
7
+ content: "";
8
+ position: absolute;
9
+ inset: 0;
10
+ border-radius: inherit;
11
+ border: 1px solid;
12
+ border-color: inherit;
13
+ transition: all 0.2s;
14
+ pointer-events: none;
15
+ }
16
+
17
+ .inside-border-x {
18
+ position: relative;
19
+ }
20
+
21
+ .inside-border-x::before {
22
+ content: "";
23
+ position: absolute;
24
+ inset: 0;
25
+ border-radius: inherit;
26
+ border-left: 1px solid;
27
+ border-right: 1px solid;
28
+ border-color: inherit;
29
+ transition: all 0.2s;
30
+ pointer-events: none;
31
+ }
32
+
33
+ .inside-border-y {
34
+ position: relative;
35
+ }
36
+
37
+ .inside-border-y::before {
38
+ content: "";
39
+ position: absolute;
40
+ inset: 0;
41
+ border-radius: inherit;
42
+ border-top: 1px solid;
43
+ border-bottom: 1px solid;
44
+ border-color: inherit;
45
+ transition: all 0.2s;
46
+ pointer-events: none;
47
+ }
styles/main.css ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Fire Design System - Must be imported first for color definitions */
2
+ @import "./fire.css";
3
+ @import "./components/index.css";
4
+ @import "./inside-border-fix.css";
5
+
6
+ /* Additional styles */
7
+ @import "additional-styles/utility-patterns.css";
8
+ @import "additional-styles/theme.css";
9
+
10
+ @tailwind base;
11
+ @tailwind components;
12
+ @tailwind utilities;
13
+
14
+ .animated {
15
+ -webkit-animation-duration: 1s;
16
+ animation-duration: 1s;
17
+ -webkit-animation-duration: 1s;
18
+ animation-duration: 1s;
19
+ -webkit-animation-fill-mode: both;
20
+ animation-fill-mode: both;
21
+ }
22
+
23
+ .height-screen-helper {
24
+ min-height: calc(100vh - 80px);
25
+ }
26
+
27
+ @layer base {
28
+ :root {
29
+ --background: 0 0% 100%;
30
+ --foreground: 240 10% 3.9%;
31
+
32
+ --card: 0 0% 100%;
33
+ --card-foreground: 240 10% 3.9%;
34
+
35
+ --popover: 0 0% 100%;
36
+ --popover-foreground: 240 10% 3.9%;
37
+
38
+ --primary: 240 5.9% 10%;
39
+ --primary-foreground: 0 0% 98%;
40
+
41
+ --secondary: 240 4.8% 95.9%;
42
+ --secondary-foreground: 240 5.9% 10%;
43
+
44
+ --muted: 240 4.8% 95.9%;
45
+ --muted-foreground: 240 3.8% 46.1%;
46
+
47
+ --accent: 240 4.8% 95.9%;
48
+ --accent-foreground: 240 5.9% 10%;
49
+
50
+ --destructive: 0 84.2% 60.2%;
51
+ --destructive-foreground: 0 0% 98%;
52
+
53
+ --border: 240 5.9% 90%;
54
+ --input: 240 5.9% 90%;
55
+ --ring: 240 10% 3.9%;
56
+
57
+ --radius: 0.5rem;
58
+ }
59
+
60
+ /* Temporarily disable dark mode until we migrate to semantic colors */
61
+ /* .dark {
62
+ --background: 240 10% 3.9%;
63
+ --foreground: 0 0% 98%;
64
+
65
+ --card: 240 10% 3.9%;
66
+ --card-foreground: 0 0% 98%;
67
+
68
+ --popover: 240 10% 3.9%;
69
+ --popover-foreground: 0 0% 98%;
70
+
71
+ --primary: 0 0% 98%;
72
+ --primary-foreground: 240 5.9% 10%;
73
+
74
+ --secondary: 240 3.7% 15.9%;
75
+ --secondary-foreground: 0 0% 98%;
76
+
77
+ --muted: 240 3.7% 15.9%;
78
+ --muted-foreground: 240 5% 64.9%;
79
+
80
+ --accent: 240 3.7% 15.9%;
81
+ --accent-foreground: 0 0% 98%;
82
+
83
+ --destructive: 0 62.8% 30.6%;
84
+ --destructive-foreground: 0 0% 98%;
85
+
86
+ --border: 240 3.7% 15.9%;
87
+ --input: 240 3.7% 15.9%;
88
+ --ring: 240 4.9% 83.9%;
89
+ } */
90
+ }
91
+
92
+ @layer utilities {
93
+ /* Hide scrollbar for Chrome, Safari and Opera */
94
+ .no-scrollbar::-webkit-scrollbar {
95
+ display: none;
96
+ }
97
+ /* Hide scrollbar for IE, Edge and Firefox */
98
+ .no-scrollbar {
99
+ -ms-overflow-style: none; /* IE and Edge */
100
+ scrollbar-width: none; /* Firefox */
101
+ }
102
+ }
103
+
104
+ .ReactModal__Overlay {
105
+ opacity: 0;
106
+ transition: opacity 300ms ease-in-out;
107
+ }
108
+
109
+ .ReactModal__Overlay--after-open {
110
+ opacity: 1;
111
+ }
112
+
113
+ .ReactModal__Overlay--before-close {
114
+ opacity: 0;
115
+ }
116
+
117
+ .DialogOverlay {
118
+ background-color: rgba(0, 0, 0, 0.5);
119
+ position: fixed;
120
+ inset: 0;
121
+ animation: overlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1);
122
+ z-index: 100;
123
+ }
124
+
125
+ .DialogContent {
126
+ background-color: white;
127
+ border-radius: 6px;
128
+ box-shadow:
129
+ hsl(206 22% 7% / 35%) 0px 10px 38px -10px,
130
+ hsl(206 22% 7% / 20%) 0px 10px 20px -15px;
131
+ position: fixed;
132
+ top: 50%;
133
+ left: 50%;
134
+ transform: translate(-50%, -50%);
135
+ width: 90vw;
136
+ max-width: 650px;
137
+ max-height: 85vh;
138
+ animation: contentShow 500ms cubic-bezier(0.16, 1, 0.3, 1);
139
+ z-index: 101;
140
+ }
141
+ .DialogContent:focus {
142
+ outline: none;
143
+ }
144
+
145
+ .DialogTitle {
146
+ margin: 0;
147
+ font-weight: 500;
148
+ color: #000;
149
+ font-size: 17px;
150
+ }
151
+
152
+ .DialogDescription {
153
+ margin: 10px 0 20px;
154
+ color: #000;
155
+ font-size: 15px;
156
+ line-height: 1.5;
157
+ }
158
+ .FireButton {
159
+ color: #f8fafc;
160
+ background-color: #111827;
161
+ width: 100%;
162
+ box-shadow:
163
+ 0 1px 3px 0 rgba(0, 0, 0, 0.1),
164
+ 0 1px 2px 0 rgba(0, 0, 0, 0.06);
165
+ transition: box-shadow 0.3s ease;
166
+ }
167
+
168
+ .FireButton:hover {
169
+ box-shadow: 0 0 8px 2px #ff5d22af;
170
+ }
171
+
172
+ @keyframes overlayShow {
173
+ from {
174
+ opacity: 0;
175
+ }
176
+ to {
177
+ opacity: 1;
178
+ }
179
+ }
180
+
181
+ @keyframes contentShow {
182
+ from {
183
+ opacity: 0;
184
+ transform: translate(-50%, -48%) scale(0.96);
185
+ }
186
+ to {
187
+ opacity: 1;
188
+ transform: translate(-50%, -50%) scale(1);
189
+ }
190
+ }
191
+
192
+ .blog-heading:target {
193
+ padding-bottom: 120px !important;
194
+ }
195
+
196
+ @layer utilities {
197
+ .extra-space {
198
+ position: relative;
199
+
200
+ &::after {
201
+ --target-size: 20px;
202
+
203
+ content: '';
204
+ position: absolute;
205
+ inset: var(--target-size);
206
+ }
207
+ }
208
+
209
+ .marquee-animation {
210
+ animation: marquee var(--speed, 10s) linear infinite
211
+ var(--direction, forwards);
212
+ }
213
+
214
+ @keyframes marquee {
215
+ to {
216
+ transform: translateX(-50%);
217
+ }
218
+ }
219
+
220
+ /* Loading state animations */
221
+ @keyframes fade-in-up {
222
+ from {
223
+ opacity: 0;
224
+ transform: translateY(20px);
225
+ }
226
+ to {
227
+ opacity: 1;
228
+ transform: translateY(0);
229
+ }
230
+ }
231
+
232
+ @keyframes pulse-subtle {
233
+ 0%, 100% {
234
+ opacity: 1;
235
+ }
236
+ 50% {
237
+ opacity: 0.95;
238
+ }
239
+ }
240
+
241
+ @keyframes text-shimmer {
242
+ 0% {
243
+ background-position: -200% center;
244
+ }
245
+ 100% {
246
+ background-position: 200% center;
247
+ }
248
+ }
249
+
250
+ .animate-fade-in-up {
251
+ animation: fade-in-up 0.6s ease-out forwards;
252
+ opacity: 0;
253
+ }
254
+
255
+ .animate-pulse-subtle {
256
+ animation: pulse-subtle 2s ease-in-out infinite;
257
+ }
258
+
259
+ .animate-text-shimmer {
260
+ background: linear-gradient(
261
+ 90deg,
262
+ rgb(156 163 175) 0%,
263
+ rgb(229 231 235) 50%,
264
+ rgb(156 163 175) 100%
265
+ );
266
+ background-size: 200% auto;
267
+ -webkit-background-clip: text;
268
+ -webkit-text-fill-color: transparent;
269
+ background-clip: text;
270
+ animation: text-shimmer 3s linear infinite;
271
+ }
272
+ }
273
+
274
+ .grecaptcha-badge { visibility: hidden; }
275
+
276
+ /* Remove all focus styles from hero input */
277
+ input[type="text"]:focus,
278
+ input[type="text"]:focus-visible {
279
+ outline: none !important;
280
+ box-shadow: none !important;
281
+ border-color: inherit !important;
282
+ }
tailwind.config.ts ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* eslint-disable @typescript-eslint/no-require-imports */
2
+ import defaultTheme from "tailwindcss/defaultTheme";
3
+ import type { Config } from "tailwindcss";
4
+
5
+ import colorsJson from "./colors.json";
6
+
7
+ const colors = Object.keys(colorsJson).reduce(
8
+ (acc, key) => {
9
+ acc[key] = `var(--${key})`;
10
+
11
+ return acc;
12
+ },
13
+ {} as Record<string, string>
14
+ );
15
+
16
+ const sizes = Array.from({ length: 1000 }, (_, i) => i).reduce(
17
+ (acc, curr) => {
18
+ acc[curr] = `${curr}px`;
19
+
20
+ return acc;
21
+ },
22
+ {
23
+ max: "max-content",
24
+ unset: "unset",
25
+ full: "100%",
26
+ inherit: "inherit",
27
+ "1/2": "50%",
28
+ "1/3": "33.3%",
29
+ "2/3": "66.6%",
30
+ "1/4": "25%",
31
+ "1/6": "16.6%",
32
+ "2/6": "33.3%",
33
+ "3/6": "50%",
34
+ "4/6": "66.6%",
35
+ "5/6": "83.3%"
36
+ } as Record<string, string>
37
+ );
38
+
39
+ const opacities = Array.from({ length: 100 }, (_, i) => i).reduce(
40
+ (acc, curr) => {
41
+ acc[curr] = curr / 100 + "";
42
+
43
+ return acc;
44
+ },
45
+ {} as Record<string, string>
46
+ );
47
+
48
+ const transitionDurations = Array.from({ length: 60 }, (_, i) => i).reduce(
49
+ (acc, curr) => {
50
+ acc[curr] = curr * 50 + "";
51
+
52
+ return acc;
53
+ },
54
+ {} as Record<string, string>
55
+ );
56
+
57
+ const themeConfig: Config = {
58
+ darkMode: "class",
59
+ content: [
60
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
61
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
62
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
63
+ "./components-new/**/*.{js,ts,jsx,tsx,mdx}",
64
+ "./styling-reference/ai-ready-website/app/**/*.{ts,tsx}",
65
+ "./styling-reference/ai-ready-website/components/**/*.{ts,tsx}",
66
+ "./styling-reference/ai-ready-website/components-new/**/*.{ts,tsx}",
67
+ ],
68
+ theme: {
69
+ container: {
70
+ center: true,
71
+ padding: "2rem",
72
+ screens: {
73
+ "2xl": "1400px",
74
+ },
75
+ },
76
+ extend: {
77
+ fontFamily: {
78
+ sans: ["var(--font-geist-sans)", "var(--font-inter)", ...defaultTheme.fontFamily.sans],
79
+ mono: ["var(--font-geist-mono)", ...defaultTheme.fontFamily.mono],
80
+ ascii: ["var(--font-roboto-mono)", ...defaultTheme.fontFamily.mono]
81
+ },
82
+ fontSize: {
83
+ "title-h1": [
84
+ "60px",
85
+ {
86
+ "lineHeight": "64px",
87
+ "letterSpacing": "-0.3px",
88
+ "fontWeight": "500"
89
+ }
90
+ ],
91
+ "title-h2": [
92
+ "52px",
93
+ {
94
+ "lineHeight": "56px",
95
+ "letterSpacing": "-0.52px",
96
+ "fontWeight": "500"
97
+ }
98
+ ],
99
+ "title-h3": [
100
+ "40px",
101
+ {
102
+ "lineHeight": "44px",
103
+ "letterSpacing": "-0.4px",
104
+ "fontWeight": "500"
105
+ }
106
+ ],
107
+ "title-h4": [
108
+ "32px",
109
+ {
110
+ "lineHeight": "36px",
111
+ "letterSpacing": "-0.32px",
112
+ "fontWeight": "500"
113
+ }
114
+ ],
115
+ "title-h5": [
116
+ "24px",
117
+ {
118
+ "lineHeight": "32px",
119
+ "letterSpacing": "-0.24px",
120
+ "fontWeight": "500"
121
+ }
122
+ ],
123
+ "body-x-large": [
124
+ "20px",
125
+ {
126
+ "lineHeight": "28px",
127
+ "letterSpacing": "-0.1px",
128
+ "fontWeight": "400"
129
+ }
130
+ ],
131
+ "body-large": [
132
+ "16px",
133
+ {
134
+ "lineHeight": "24px",
135
+ "letterSpacing": "0px",
136
+ "fontWeight": "400"
137
+ }
138
+ ],
139
+ "body-medium": [
140
+ "14px",
141
+ {
142
+ "lineHeight": "20px",
143
+ "letterSpacing": "0.14px",
144
+ "fontWeight": "400"
145
+ }
146
+ ],
147
+ "body-small": [
148
+ "13px",
149
+ {
150
+ "lineHeight": "20px",
151
+ "letterSpacing": "0px",
152
+ "fontWeight": "400"
153
+ }
154
+ ],
155
+ "body-input": [
156
+ "15px",
157
+ {
158
+ "lineHeight": "24px",
159
+ "letterSpacing": "0px",
160
+ "fontWeight": "400"
161
+ }
162
+ ],
163
+ "label-x-large": [
164
+ "20px",
165
+ {
166
+ "lineHeight": "28px",
167
+ "letterSpacing": "-0.1px",
168
+ "fontWeight": "450"
169
+ }
170
+ ],
171
+ "label-large": [
172
+ "16px",
173
+ {
174
+ "lineHeight": "24px",
175
+ "letterSpacing": "0px",
176
+ "fontWeight": "450"
177
+ }
178
+ ],
179
+ "label-medium": [
180
+ "14px",
181
+ {
182
+ "lineHeight": "20px",
183
+ "letterSpacing": "0px",
184
+ "fontWeight": "450"
185
+ }
186
+ ],
187
+ "label-small": [
188
+ "13px",
189
+ {
190
+ "lineHeight": "20px",
191
+ "letterSpacing": "0px",
192
+ "fontWeight": "450"
193
+ }
194
+ ],
195
+ "label-x-small": [
196
+ "12px",
197
+ {
198
+ "lineHeight": "20px",
199
+ "letterSpacing": "0px",
200
+ "fontWeight": "450"
201
+ }
202
+ ],
203
+ "mono-medium": [
204
+ "14px",
205
+ {
206
+ "lineHeight": "22px",
207
+ "letterSpacing": "0px",
208
+ "fontWeight": "400"
209
+ }
210
+ ],
211
+ "mono-small": [
212
+ "13px",
213
+ {
214
+ "lineHeight": "20px",
215
+ "letterSpacing": "0px",
216
+ "fontWeight": "500"
217
+ }
218
+ ],
219
+ "mono-x-small": [
220
+ "12px",
221
+ {
222
+ "lineHeight": "16px",
223
+ "letterSpacing": "0px",
224
+ "fontWeight": "400"
225
+ }
226
+ ],
227
+ "title-blog": [
228
+ "28px",
229
+ {
230
+ "lineHeight": "36px",
231
+ "letterSpacing": "-0.28px",
232
+ "fontWeight": "500"
233
+ }
234
+ ]
235
+ },
236
+ colors: {
237
+ transparent: "transparent",
238
+ current: "currentColor",
239
+ border: "hsl(var(--border))",
240
+ input: "hsl(var(--input))",
241
+ ring: "hsl(var(--ring))",
242
+ background: "hsl(var(--background))",
243
+ foreground: "hsl(var(--foreground))",
244
+ primary: {
245
+ DEFAULT: "hsl(var(--primary))",
246
+ foreground: "hsl(var(--primary-foreground))",
247
+ },
248
+ secondary: {
249
+ DEFAULT: "hsl(var(--secondary))",
250
+ foreground: "hsl(var(--secondary-foreground))",
251
+ },
252
+ destructive: {
253
+ DEFAULT: "hsl(var(--destructive))",
254
+ foreground: "hsl(var(--destructive-foreground))",
255
+ },
256
+ muted: {
257
+ DEFAULT: "hsl(var(--muted))",
258
+ foreground: "hsl(var(--muted-foreground))",
259
+ },
260
+ accent: {
261
+ DEFAULT: "hsl(var(--accent))",
262
+ foreground: "hsl(var(--accent-foreground))",
263
+ },
264
+ popover: {
265
+ DEFAULT: "hsl(var(--popover))",
266
+ foreground: "hsl(var(--popover-foreground))",
267
+ },
268
+ card: {
269
+ DEFAULT: "hsl(var(--card))",
270
+ foreground: "hsl(var(--card-foreground))",
271
+ },
272
+ ...colors
273
+ },
274
+ screens: {
275
+ xs: { min: "390px" },
276
+ "xs-max": { max: "389px" },
277
+ sm: { min: "576px" },
278
+ "sm-max": { max: "575px" },
279
+ md: { min: "768px" },
280
+ "md-max": { max: "767px" },
281
+ lg: { min: "996px" },
282
+ "lg-max": { max: "995px" },
283
+ xl: { min: "1200px" },
284
+ "xl-max": { max: "1199px" }
285
+ },
286
+ opacity: opacities,
287
+ spacing: {
288
+ ...sizes,
289
+ 'root': 'var(--root-padding)'
290
+ },
291
+ width: sizes,
292
+ maxWidth: sizes,
293
+ height: sizes,
294
+ inset: sizes,
295
+ borderWidth: sizes,
296
+ backdropBlur: Array.from({ length: 20 }, (_, i) => i).reduce(
297
+ (acc, curr) => {
298
+ acc[curr] = curr + "px";
299
+
300
+ return acc;
301
+ },
302
+ {} as Record<string, string>
303
+ ),
304
+ transitionTimingFunction: { DEFAULT: "cubic-bezier(0.25, 0.1, 0.25, 1)" },
305
+ transitionDuration: {
306
+ DEFAULT: "200ms",
307
+ ...transitionDurations
308
+ },
309
+ transitionDelay: {
310
+ ...transitionDurations
311
+ },
312
+ borderRadius: (() => {
313
+ const radius: Record<string | number, string> = {
314
+ full: "999px",
315
+ inherit: "inherit",
316
+ 0: "0px",
317
+ lg: "var(--radius)",
318
+ md: "calc(var(--radius) - 2px)",
319
+ sm: "calc(var(--radius) - 4px)",
320
+ };
321
+
322
+ for (let i = 1; i <= 32; i += 1) {
323
+ radius[i] = `${i}px`;
324
+ }
325
+
326
+ return radius;
327
+ })()
328
+ }
329
+ },
330
+ plugins: [
331
+ ({
332
+ addUtilities, matchUtilities
333
+ }: any) => {
334
+ addUtilities({
335
+ // Inside-border utilities are defined in inside-border-fix.css to avoid Tailwind variant conflicts
336
+ '.mask-border': {
337
+ "mask": "linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)",
338
+ "mask-composite": "exclude",
339
+ "pointer-events": "none"
340
+ },
341
+ ".center-x": { "@apply absolute left-1/2 -translate-x-1/2": {} },
342
+ ".center-y": { "@apply absolute top-1/2 -translate-y-1/2": {} },
343
+ ".center": { "@apply absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2": {} },
344
+ ".flex-center": { "@apply flex items-center justify-center": {} },
345
+ ".overlay": { "@apply absolute top-0 left-0 w-full h-full rounded-inherit": {} },
346
+ ".text-gradient": { "@apply !bg-clip-text !text-transparent": {} }
347
+ });
348
+ matchUtilities(
349
+ {
350
+ 'cw': (value: string) => {
351
+ const width = parseInt(value);
352
+
353
+ return {
354
+ width: value,
355
+ left: `calc(50% - ${width / 2}px)`
356
+ };
357
+ },
358
+ 'ch': (value: string) => {
359
+ const height = parseInt(value);
360
+
361
+ return {
362
+ height: value,
363
+ top: `calc(50% - ${height / 2}px)`
364
+ };
365
+ },
366
+ 'cs': (value: string) => {
367
+ const size = parseInt(value);
368
+
369
+ return {
370
+ width: size,
371
+ height: size,
372
+ left: `calc(50% - ${size / 2}px)`,
373
+ top: `calc(50% - ${size / 2}px)`
374
+ };
375
+ },
376
+ 'cmw': (value: string) => {
377
+ const [maxWidth, paddingX] = value.split(',').map((v) => parseInt(v));
378
+
379
+ const width = paddingX ? `calc(100% - ${paddingX * 2}px)` : '100%';
380
+
381
+ return {
382
+ maxWidth: maxWidth,
383
+ width,
384
+ left: `calc(50% - (min(${maxWidth}px, ${width}) / 2))`
385
+ };
386
+ },
387
+ 'mw': (value: string) => {
388
+ const [maxWidth, paddingX] = value.split(',').map((v) => parseInt(v));
389
+
390
+ const width = paddingX ? `calc(100% - ${paddingX * 2}px)` : '100%';
391
+
392
+ return {
393
+ maxWidth: maxWidth,
394
+ width
395
+ };
396
+ }
397
+ },
398
+ { values: sizes }
399
+ );
400
+ },
401
+ require("tailwind-gradient-mask-image"),
402
+ require("@tailwindcss/typography"),
403
+ ]
404
+ };
405
+
406
+ export default themeConfig;
tsconfig.json ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "module": "esnext",
11
+ "moduleResolution": "bundler",
12
+ "resolveJsonModule": true,
13
+ "isolatedModules": true,
14
+ "jsx": "preserve",
15
+ "incremental": true,
16
+ "plugins": [
17
+ {
18
+ "name": "next"
19
+ }
20
+ ],
21
+ "paths": {
22
+ "@/*": ["./*"]
23
+ }
24
+ },
25
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
26
+ "exclude": ["node_modules", "examples", "tests", "lib/e2b-backends/archive", "styles"]
27
+ }
types/conversation.ts ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Conversation tracking types for maintaining context across interactions
2
+
3
+ export interface ConversationMessage {
4
+ id: string;
5
+ role: 'user' | 'assistant';
6
+ content: string;
7
+ timestamp: number;
8
+ metadata?: {
9
+ editedFiles?: string[]; // Files edited in this interaction
10
+ addedPackages?: string[]; // Packages added in this interaction
11
+ editType?: string; // Type of edit performed
12
+ sandboxId?: string; // Sandbox ID at time of message
13
+ };
14
+ }
15
+
16
+ export interface ConversationEdit {
17
+ timestamp: number;
18
+ userRequest: string;
19
+ editType: string;
20
+ targetFiles: string[];
21
+ confidence: number;
22
+ outcome: 'success' | 'partial' | 'failed';
23
+ errorMessage?: string;
24
+ }
25
+
26
+ export interface ConversationContext {
27
+ messages: ConversationMessage[];
28
+ edits: ConversationEdit[];
29
+ currentTopic?: string; // Current focus area (e.g., "header styling", "hero section")
30
+ projectEvolution: {
31
+ initialState?: string; // Description of initial project state
32
+ majorChanges: Array<{
33
+ timestamp: number;
34
+ description: string;
35
+ filesAffected: string[];
36
+ }>;
37
+ };
38
+ userPreferences: {
39
+ editStyle?: 'targeted' | 'comprehensive'; // How the user prefers edits
40
+ commonRequests?: string[]; // Common patterns in user requests
41
+ packagePreferences?: string[]; // Commonly used packages
42
+ };
43
+ }
44
+
45
+ export interface ConversationState {
46
+ conversationId: string;
47
+ startedAt: number;
48
+ lastUpdated: number;
49
+ context: ConversationContext;
50
+ }
types/file-manifest.ts ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // File manifest types for enhanced edit tracking
2
+
3
+ export interface FileInfo {
4
+ content: string;
5
+ type: 'component' | 'page' | 'style' | 'config' | 'utility' | 'layout' | 'hook' | 'context';
6
+ exports?: string[]; // Named exports and default export
7
+ imports?: ImportInfo[]; // Dependencies
8
+ lastModified: number;
9
+ componentInfo?: ComponentInfo; // For React components
10
+ path: string;
11
+ relativePath: string; // Path relative to src/
12
+ }
13
+
14
+ export interface ImportInfo {
15
+ source: string; // e.g., './Header', 'react', '@/components/Button'
16
+ imports: string[]; // Named imports
17
+ defaultImport?: string; // Default import name
18
+ isLocal: boolean; // true if starts with './' or '@/'
19
+ }
20
+
21
+ export interface ComponentInfo {
22
+ name: string;
23
+ props?: string[]; // Prop names if detectable
24
+ hooks?: string[]; // Hooks used (useState, useEffect, etc)
25
+ hasState: boolean;
26
+ childComponents?: string[]; // Components rendered inside
27
+ }
28
+
29
+ export interface RouteInfo {
30
+ path: string; // Route path (e.g., '/videos', '/about')
31
+ component: string; // Component file path
32
+ layout?: string; // Layout component if any
33
+ }
34
+
35
+ export interface ComponentTree {
36
+ [componentName: string]: {
37
+ file: string;
38
+ imports: string[]; // Components it imports
39
+ importedBy: string[]; // Components that import it
40
+ type: 'page' | 'layout' | 'component';
41
+ }
42
+ }
43
+
44
+ export interface FileManifest {
45
+ files: Record<string, FileInfo>;
46
+ routes: RouteInfo[];
47
+ componentTree: ComponentTree;
48
+ entryPoint: string; // Usually App.jsx or main.jsx
49
+ styleFiles: string[]; // All CSS files
50
+ timestamp: number;
51
+ }
52
+
53
+ // Edit classification types
54
+ export enum EditType {
55
+ UPDATE_COMPONENT = 'UPDATE_COMPONENT', // "update the header", "change button color"
56
+ ADD_FEATURE = 'ADD_FEATURE', // "add a videos page", "create new component"
57
+ FIX_ISSUE = 'FIX_ISSUE', // "fix the styling", "resolve error"
58
+ REFACTOR = 'REFACTOR', // "reorganize", "clean up"
59
+ FULL_REBUILD = 'FULL_REBUILD', // "start over", "recreate everything"
60
+ UPDATE_STYLE = 'UPDATE_STYLE', // "change colors", "update theme"
61
+ ADD_DEPENDENCY = 'ADD_DEPENDENCY' // "install package", "add library"
62
+ }
63
+
64
+ export interface EditIntent {
65
+ type: EditType;
66
+ targetFiles: string[]; // Predicted files to edit
67
+ confidence: number; // 0-1 confidence score
68
+ description: string; // Human-readable description
69
+ suggestedContext: string[]; // Additional files to include for context
70
+ }
71
+
72
+ // Patterns for intent detection
73
+ export interface IntentPattern {
74
+ patterns: RegExp[];
75
+ type: EditType;
76
+ fileResolver: (prompt: string, manifest: FileManifest) => string[];
77
+ }
types/sandbox.ts ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Global types for sandbox file management
2
+
3
+ export interface SandboxFile {
4
+ content: string;
5
+ lastModified: number;
6
+ }
7
+
8
+ export interface SandboxFileCache {
9
+ files: Record<string, SandboxFile>;
10
+ lastSync: number;
11
+ sandboxId: string;
12
+ manifest?: any; // FileManifest type from file-manifest.ts
13
+ }
14
+
15
+ export interface SandboxState {
16
+ fileCache: SandboxFileCache | null;
17
+ sandbox: any; // E2B sandbox instance
18
+ sandboxData: {
19
+ sandboxId: string;
20
+ url: string;
21
+ } | null;
22
+ }
23
+
24
+ // Declare global types
25
+ declare global {
26
+ var activeSandbox: any;
27
+ var sandboxState: SandboxState;
28
+ var existingFiles: Set<string>;
29
+ }
30
+
31
+ export {};
utils/cn.ts ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ import classNames from "classnames";
2
+
3
+ export function cn(...classes: classNames.ArgumentArray) {
4
+ return classNames(...classes);
5
+ }