Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- app/api/create-ai-sandbox-v2/route.ts +12 -6
- app/api/submit-job/route.ts +27 -22
- app/generation/page.tsx +34 -5
- app/page.tsx +53 -6
- lib/arxiv.ts +63 -0
- lib/sandbox/providers/e2b-provider.ts +41 -0
- lib/sandbox/types.ts +5 -0
- package.json +2 -0
- pnpm-lock.yaml +36 -5
app/api/create-ai-sandbox-v2/route.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import { NextResponse } from 'next/server';
|
| 2 |
import { SandboxFactory } from '@/lib/sandbox/factory';
|
| 3 |
// SandboxProvider type is used through SandboxFactory
|
| 4 |
import type { SandboxState } from '@/types/sandbox';
|
|
@@ -12,9 +12,10 @@ declare global {
|
|
| 12 |
var sandboxState: SandboxState;
|
| 13 |
}
|
| 14 |
|
| 15 |
-
export async function POST() {
|
| 16 |
try {
|
| 17 |
-
|
|
|
|
| 18 |
|
| 19 |
// Clean up all existing sandboxes
|
| 20 |
console.log('[create-ai-sandbox-v2] Cleaning up existing sandboxes...');
|
|
@@ -41,8 +42,13 @@ export async function POST() {
|
|
| 41 |
const provider = SandboxFactory.create();
|
| 42 |
const sandboxInfo = await provider.createSandbox();
|
| 43 |
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
// Register with sandbox manager
|
| 48 |
sandboxManager.registerSandbox(sandboxInfo.sandboxId, provider);
|
|
@@ -75,7 +81,7 @@ export async function POST() {
|
|
| 75 |
sandboxId: sandboxInfo.sandboxId,
|
| 76 |
url: sandboxInfo.url,
|
| 77 |
provider: sandboxInfo.provider,
|
| 78 |
-
message:
|
| 79 |
});
|
| 80 |
|
| 81 |
} catch (error) {
|
|
|
|
| 1 |
+
import { NextRequest, NextResponse } from 'next/server';
|
| 2 |
import { SandboxFactory } from '@/lib/sandbox/factory';
|
| 3 |
// SandboxProvider type is used through SandboxFactory
|
| 4 |
import type { SandboxState } from '@/types/sandbox';
|
|
|
|
| 12 |
var sandboxState: SandboxState;
|
| 13 |
}
|
| 14 |
|
| 15 |
+
export async function POST(request: NextRequest) {
|
| 16 |
try {
|
| 17 |
+
const { runtime = 'react' } = await request.json() || {};
|
| 18 |
+
console.log(`[create-ai-sandbox-v2] Creating sandbox for ${runtime}...`);
|
| 19 |
|
| 20 |
// Clean up all existing sandboxes
|
| 21 |
console.log('[create-ai-sandbox-v2] Cleaning up existing sandboxes...');
|
|
|
|
| 42 |
const provider = SandboxFactory.create();
|
| 43 |
const sandboxInfo = await provider.createSandbox();
|
| 44 |
|
| 45 |
+
if (runtime === 'python') {
|
| 46 |
+
console.log('[create-ai-sandbox-v2] Setting up Python environment...');
|
| 47 |
+
await provider.setupPythonEnv();
|
| 48 |
+
} else {
|
| 49 |
+
console.log('[create-ai-sandbox-v2] Setting up Vite React app...');
|
| 50 |
+
await provider.setupViteApp();
|
| 51 |
+
}
|
| 52 |
|
| 53 |
// Register with sandbox manager
|
| 54 |
sandboxManager.registerSandbox(sandboxInfo.sandboxId, provider);
|
|
|
|
| 81 |
sandboxId: sandboxInfo.sandboxId,
|
| 82 |
url: sandboxInfo.url,
|
| 83 |
provider: sandboxInfo.provider,
|
| 84 |
+
message: `Sandbox created and ${runtime} initialized`
|
| 85 |
});
|
| 86 |
|
| 87 |
} catch (error) {
|
app/api/submit-job/route.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { getProviderForModel } from "@/lib/ai/provider-manager";
|
|
| 4 |
import { extractFilesFromAI, sanitizeBranchName } from "@/lib/ai-utils";
|
| 5 |
import { pushToGitHub } from "@/lib/github";
|
| 6 |
import { appConfig } from "@/config/app.config";
|
|
|
|
| 7 |
|
| 8 |
export async function GET() {
|
| 9 |
return NextResponse.json({ message: "Submit job endpoint is active. Use POST to submit a paper." });
|
|
@@ -15,7 +16,7 @@ export async function POST(request: NextRequest) {
|
|
| 15 |
arxivUrl,
|
| 16 |
paperContent,
|
| 17 |
repo,
|
| 18 |
-
paperName,
|
| 19 |
model = appConfig.ai.defaultModel
|
| 20 |
} = await request.json();
|
| 21 |
|
|
@@ -23,37 +24,41 @@ export async function POST(request: NextRequest) {
|
|
| 23 |
return NextResponse.json({ error: "Target GitHub repository is required (e.g. 'owner/repo')" }, { status: 400 });
|
| 24 |
}
|
| 25 |
|
| 26 |
-
if (!paperName) {
|
| 27 |
-
return NextResponse.json({ error: "Paper name is required for branch naming" }, { status: 400 });
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
const githubToken = process.env.PERSONAL_ACCESS_TOKEN;
|
| 31 |
if (!githubToken) {
|
| 32 |
return NextResponse.json({ error: "GitHub PERSONAL_ACCESS_TOKEN not configured in environment" }, { status: 500 });
|
| 33 |
}
|
| 34 |
|
| 35 |
let finalPaperContent = paperContent || "";
|
|
|
|
| 36 |
|
| 37 |
-
// 1. Get paper content from Arxiv if URL is provided
|
| 38 |
if (arxivUrl && !paperContent) {
|
| 39 |
-
console.log(`[submit-job]
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
if
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
}
|
|
|
|
|
|
|
| 54 |
}
|
| 55 |
-
} catch (error) {
|
| 56 |
-
console.error(`[submit-job] Error calling scrape API:`, error);
|
| 57 |
}
|
| 58 |
}
|
| 59 |
|
|
|
|
| 4 |
import { extractFilesFromAI, sanitizeBranchName } from "@/lib/ai-utils";
|
| 5 |
import { pushToGitHub } from "@/lib/github";
|
| 6 |
import { appConfig } from "@/config/app.config";
|
| 7 |
+
import { fetchArxivPaper } from "@/lib/arxiv";
|
| 8 |
|
| 9 |
export async function GET() {
|
| 10 |
return NextResponse.json({ message: "Submit job endpoint is active. Use POST to submit a paper." });
|
|
|
|
| 16 |
arxivUrl,
|
| 17 |
paperContent,
|
| 18 |
repo,
|
| 19 |
+
paperName: providedPaperName,
|
| 20 |
model = appConfig.ai.defaultModel
|
| 21 |
} = await request.json();
|
| 22 |
|
|
|
|
| 24 |
return NextResponse.json({ error: "Target GitHub repository is required (e.g. 'owner/repo')" }, { status: 400 });
|
| 25 |
}
|
| 26 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
const githubToken = process.env.PERSONAL_ACCESS_TOKEN;
|
| 28 |
if (!githubToken) {
|
| 29 |
return NextResponse.json({ error: "GitHub PERSONAL_ACCESS_TOKEN not configured in environment" }, { status: 500 });
|
| 30 |
}
|
| 31 |
|
| 32 |
let finalPaperContent = paperContent || "";
|
| 33 |
+
let paperName = providedPaperName || "paper-to-code";
|
| 34 |
|
| 35 |
+
// 1. Get paper content from Arxiv API if URL or query is provided
|
| 36 |
if (arxivUrl && !paperContent) {
|
| 37 |
+
console.log(`[submit-job] Fetching Arxiv paper: ${arxivUrl}`);
|
| 38 |
+
const paper = await fetchArxivPaper(arxivUrl);
|
| 39 |
+
if (paper) {
|
| 40 |
+
finalPaperContent = `Title: ${paper.title}\n\nAuthors: ${paper.authors.join(', ')}\n\nSummary: ${paper.summary}`;
|
| 41 |
+
paperName = paper.title;
|
| 42 |
+
console.log(`[submit-job] Successfully fetched: ${paper.title}`);
|
| 43 |
+
} else {
|
| 44 |
+
console.warn(`[submit-job] Arxiv API failed for ${arxivUrl}, falling back to scraping`);
|
| 45 |
+
// Fallback to scrape if API fails
|
| 46 |
+
try {
|
| 47 |
+
const scrapeResponse = await fetch(`${process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3000'}/api/scrape-website`, {
|
| 48 |
+
method: 'POST',
|
| 49 |
+
headers: { 'Content-Type': 'application/json' },
|
| 50 |
+
body: JSON.stringify({ url: arxivUrl })
|
| 51 |
+
});
|
| 52 |
+
|
| 53 |
+
if (scrapeResponse.ok) {
|
| 54 |
+
const scrapeData = await scrapeResponse.json();
|
| 55 |
+
if (scrapeData.success) {
|
| 56 |
+
finalPaperContent = scrapeData.data.content;
|
| 57 |
+
}
|
| 58 |
}
|
| 59 |
+
} catch (error) {
|
| 60 |
+
console.error(`[submit-job] Error calling scrape API:`, error);
|
| 61 |
}
|
|
|
|
|
|
|
| 62 |
}
|
| 63 |
}
|
| 64 |
|
app/generation/page.tsx
CHANGED
|
@@ -61,6 +61,7 @@ interface ScrapeData {
|
|
| 61 |
}
|
| 62 |
|
| 63 |
function AISandboxPage() {
|
|
|
|
| 64 |
const [sandboxData, setSandboxData] = useState<SandboxData | null>(null);
|
| 65 |
const [loading, setLoading] = useState(false);
|
| 66 |
const [status, setStatus] = useState({ text: 'Not connected', active: false });
|
|
@@ -529,18 +530,20 @@ function AISandboxPage() {
|
|
| 529 |
|
| 530 |
const sandboxCreationRef = useRef<boolean>(false);
|
| 531 |
|
| 532 |
-
const createSandbox = async (fromHomeScreen = false) => {
|
| 533 |
// Prevent duplicate sandbox creation
|
| 534 |
if (sandboxCreationRef.current) {
|
| 535 |
console.log('[createSandbox] Sandbox creation already in progress, skipping...');
|
| 536 |
return null;
|
| 537 |
}
|
| 538 |
|
|
|
|
|
|
|
| 539 |
sandboxCreationRef.current = true;
|
| 540 |
-
console.log(
|
| 541 |
setLoading(true);
|
| 542 |
setShowLoadingBackground(true);
|
| 543 |
-
updateStatus(
|
| 544 |
setResponseArea([]);
|
| 545 |
setScreenshotError(null);
|
| 546 |
|
|
@@ -548,7 +551,7 @@ function AISandboxPage() {
|
|
| 548 |
const response = await fetch('/api/create-ai-sandbox-v2', {
|
| 549 |
method: 'POST',
|
| 550 |
headers: { 'Content-Type': 'application/json' },
|
| 551 |
-
body: JSON.stringify({})
|
| 552 |
});
|
| 553 |
|
| 554 |
const data = await response.json();
|
|
@@ -3282,7 +3285,33 @@ Focus on the key sections and content, making it clean and modern.`;
|
|
| 3282 |
<HeaderProvider>
|
| 3283 |
<div className="font-sans bg-background text-foreground h-screen flex flex-col">
|
| 3284 |
<div className="bg-white py-[15px] py-[8px] border-b border-border-faint flex items-center justify-between shadow-sm">
|
| 3285 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3286 |
<div className="flex items-center gap-2">
|
| 3287 |
{/* Model Selector - Left side */}
|
| 3288 |
<select
|
|
|
|
| 61 |
}
|
| 62 |
|
| 63 |
function AISandboxPage() {
|
| 64 |
+
const [mode, setMode] = useState<'website' | 'paper'>('website');
|
| 65 |
const [sandboxData, setSandboxData] = useState<SandboxData | null>(null);
|
| 66 |
const [loading, setLoading] = useState(false);
|
| 67 |
const [status, setStatus] = useState({ text: 'Not connected', active: false });
|
|
|
|
| 530 |
|
| 531 |
const sandboxCreationRef = useRef<boolean>(false);
|
| 532 |
|
| 533 |
+
const createSandbox = async (fromHomeScreen = false, runtimeOverride?: string) => {
|
| 534 |
// Prevent duplicate sandbox creation
|
| 535 |
if (sandboxCreationRef.current) {
|
| 536 |
console.log('[createSandbox] Sandbox creation already in progress, skipping...');
|
| 537 |
return null;
|
| 538 |
}
|
| 539 |
|
| 540 |
+
const runtime = runtimeOverride || (mode === 'paper' ? 'python' : 'react');
|
| 541 |
+
|
| 542 |
sandboxCreationRef.current = true;
|
| 543 |
+
console.log(`[createSandbox] Starting sandbox creation for ${runtime}...`);
|
| 544 |
setLoading(true);
|
| 545 |
setShowLoadingBackground(true);
|
| 546 |
+
updateStatus(`Creating ${runtime} sandbox...`, false);
|
| 547 |
setResponseArea([]);
|
| 548 |
setScreenshotError(null);
|
| 549 |
|
|
|
|
| 551 |
const response = await fetch('/api/create-ai-sandbox-v2', {
|
| 552 |
method: 'POST',
|
| 553 |
headers: { 'Content-Type': 'application/json' },
|
| 554 |
+
body: JSON.stringify({ runtime })
|
| 555 |
});
|
| 556 |
|
| 557 |
const data = await response.json();
|
|
|
|
| 3285 |
<HeaderProvider>
|
| 3286 |
<div className="font-sans bg-background text-foreground h-screen flex flex-col">
|
| 3287 |
<div className="bg-white py-[15px] py-[8px] border-b border-border-faint flex items-center justify-between shadow-sm">
|
| 3288 |
+
<div className="flex items-center gap-4">
|
| 3289 |
+
<HeaderBrandKit />
|
| 3290 |
+
<div className="bg-black-alpha-4 p-2 rounded-8 flex gap-2 ml-4">
|
| 3291 |
+
<button
|
| 3292 |
+
onClick={() => {
|
| 3293 |
+
if (mode !== 'website') {
|
| 3294 |
+
setMode('website');
|
| 3295 |
+
setSandboxData(null); // Reset sandbox when switching modes
|
| 3296 |
+
}
|
| 3297 |
+
}}
|
| 3298 |
+
className={`px-12 py-4 rounded-6 text-xs font-medium transition-all ${mode === 'website' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
|
| 3299 |
+
>
|
| 3300 |
+
Website2Code
|
| 3301 |
+
</button>
|
| 3302 |
+
<button
|
| 3303 |
+
onClick={() => {
|
| 3304 |
+
if (mode !== 'paper') {
|
| 3305 |
+
setMode('paper');
|
| 3306 |
+
setSandboxData(null); // Reset sandbox when switching modes
|
| 3307 |
+
}
|
| 3308 |
+
}}
|
| 3309 |
+
className={`px-12 py-4 rounded-6 text-xs font-medium transition-all ${mode === 'paper' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
|
| 3310 |
+
>
|
| 3311 |
+
Paper2Code
|
| 3312 |
+
</button>
|
| 3313 |
+
</div>
|
| 3314 |
+
</div>
|
| 3315 |
<div className="flex items-center gap-2">
|
| 3316 |
{/* Model Selector - Left side */}
|
| 3317 |
<select
|
app/page.tsx
CHANGED
|
@@ -38,6 +38,7 @@ interface SearchResult {
|
|
| 38 |
}
|
| 39 |
|
| 40 |
export default function HomePage() {
|
|
|
|
| 41 |
const [url, setUrl] = useState<string>("");
|
| 42 |
const [selectedStyle, setSelectedStyle] = useState<string>("1");
|
| 43 |
const [selectedModel, setSelectedModel] = useState<string>(appConfig.ai.defaultModel);
|
|
@@ -87,7 +88,34 @@ export default function HomePage() {
|
|
| 87 |
const inputValue = url.trim();
|
| 88 |
|
| 89 |
if (!inputValue) {
|
| 90 |
-
toast.error("Please enter a URL or search term");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
return;
|
| 92 |
}
|
| 93 |
|
|
@@ -260,10 +288,29 @@ export default function HomePage() {
|
|
| 260 |
<HomeHeroBackground />
|
| 261 |
|
| 262 |
<div className="relative container px-16">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 263 |
<HomeHeroBadge />
|
| 264 |
<HomeHeroTitle />
|
| 265 |
<p className="text-center text-body-large">
|
| 266 |
-
|
|
|
|
|
|
|
| 267 |
</p>
|
| 268 |
<Link
|
| 269 |
className="bg-black-alpha-4 hover:bg-black-alpha-6 rounded-6 px-8 lg:px-6 text-label-large h-30 lg:h-24 block mt-8 mx-auto w-max gap-4 transition-all"
|
|
@@ -378,7 +425,7 @@ export default function HomePage() {
|
|
| 378 |
)}
|
| 379 |
<input
|
| 380 |
className="flex-1 bg-transparent text-body-input text-accent-black placeholder:text-black-alpha-48 focus:outline-none focus:ring-0 focus:border-transparent"
|
| 381 |
-
placeholder="Enter URL or search term..."
|
| 382 |
type="text"
|
| 383 |
value={url}
|
| 384 |
disabled={isSearching}
|
|
@@ -416,7 +463,7 @@ export default function HomePage() {
|
|
| 416 |
>
|
| 417 |
<HeroInputSubmitButton
|
| 418 |
dirty={url.length > 0}
|
| 419 |
-
buttonText={isURL(url) ? 'Scrape Site' : 'Search'}
|
| 420 |
disabled={isSearching}
|
| 421 |
/>
|
| 422 |
</div>
|
|
@@ -424,9 +471,9 @@ export default function HomePage() {
|
|
| 424 |
)}
|
| 425 |
</div>
|
| 426 |
|
| 427 |
-
{/* Options Section - Only show when valid URL */}
|
| 428 |
<div className={`overflow-hidden transition-all duration-500 ease-in-out ${
|
| 429 |
-
isValidUrl ? (extendBrandStyles ? 'max-h-[400px]' : 'max-h-[300px]') + ' opacity-100' : 'max-h-0 opacity-0'
|
| 430 |
}`}>
|
| 431 |
<div className="px-[28px] pt-0 pb-[28px]">
|
| 432 |
<div className="border-t border-gray-100 bg-white">
|
|
|
|
| 38 |
}
|
| 39 |
|
| 40 |
export default function HomePage() {
|
| 41 |
+
const [mode, setMode] = useState<'website' | 'paper'>('website');
|
| 42 |
const [url, setUrl] = useState<string>("");
|
| 43 |
const [selectedStyle, setSelectedStyle] = useState<string>("1");
|
| 44 |
const [selectedModel, setSelectedModel] = useState<string>(appConfig.ai.defaultModel);
|
|
|
|
| 88 |
const inputValue = url.trim();
|
| 89 |
|
| 90 |
if (!inputValue) {
|
| 91 |
+
toast.error(mode === 'website' ? "Please enter a URL or search term" : "Please enter an Arxiv link or paper query");
|
| 92 |
+
return;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
if (mode === 'paper') {
|
| 96 |
+
toast.info("Transforming paper to code...");
|
| 97 |
+
try {
|
| 98 |
+
const response = await fetch('/api/submit-job', {
|
| 99 |
+
method: 'POST',
|
| 100 |
+
headers: { 'Content-Type': 'application/json' },
|
| 101 |
+
body: JSON.stringify({
|
| 102 |
+
arxivUrl: inputValue,
|
| 103 |
+
repo: 'AUXteam/Paper2Code-Jobs', // Default repo for paper jobs
|
| 104 |
+
paperName: inputValue.split('/').pop() || 'paper'
|
| 105 |
+
})
|
| 106 |
+
});
|
| 107 |
+
const data = await response.json();
|
| 108 |
+
if (data.success) {
|
| 109 |
+
toast.success("Job submitted! Code will be pushed to GitHub.");
|
| 110 |
+
if (data.githubUrl) {
|
| 111 |
+
window.open(data.githubUrl, '_blank');
|
| 112 |
+
}
|
| 113 |
+
} else {
|
| 114 |
+
toast.error(data.error || "Failed to submit job");
|
| 115 |
+
}
|
| 116 |
+
} catch (error) {
|
| 117 |
+
toast.error("An error occurred while submitting the paper");
|
| 118 |
+
}
|
| 119 |
return;
|
| 120 |
}
|
| 121 |
|
|
|
|
| 288 |
<HomeHeroBackground />
|
| 289 |
|
| 290 |
<div className="relative container px-16">
|
| 291 |
+
<div className="flex justify-center mb-12">
|
| 292 |
+
<div className="bg-black-alpha-4 p-4 rounded-12 flex gap-4">
|
| 293 |
+
<button
|
| 294 |
+
onClick={() => setMode('website')}
|
| 295 |
+
className={`px-16 py-8 rounded-8 text-label-medium transition-all ${mode === 'website' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
|
| 296 |
+
>
|
| 297 |
+
Website2Code
|
| 298 |
+
</button>
|
| 299 |
+
<button
|
| 300 |
+
onClick={() => setMode('paper')}
|
| 301 |
+
className={`px-16 py-8 rounded-8 text-label-medium transition-all ${mode === 'paper' ? 'bg-white shadow-sm text-accent-black' : 'text-black-alpha-48 hover:text-black-alpha-72'}`}
|
| 302 |
+
>
|
| 303 |
+
Paper2Code
|
| 304 |
+
</button>
|
| 305 |
+
</div>
|
| 306 |
+
</div>
|
| 307 |
+
|
| 308 |
<HomeHeroBadge />
|
| 309 |
<HomeHeroTitle />
|
| 310 |
<p className="text-center text-body-large">
|
| 311 |
+
{mode === 'website'
|
| 312 |
+
? "Clone brand format or re-imagine any website, in seconds."
|
| 313 |
+
: "Transform research papers into functional Python or React code."}
|
| 314 |
</p>
|
| 315 |
<Link
|
| 316 |
className="bg-black-alpha-4 hover:bg-black-alpha-6 rounded-6 px-8 lg:px-6 text-label-large h-30 lg:h-24 block mt-8 mx-auto w-max gap-4 transition-all"
|
|
|
|
| 425 |
)}
|
| 426 |
<input
|
| 427 |
className="flex-1 bg-transparent text-body-input text-accent-black placeholder:text-black-alpha-48 focus:outline-none focus:ring-0 focus:border-transparent"
|
| 428 |
+
placeholder={mode === 'website' ? "Enter URL or search term..." : "Enter Arxiv link or reference..."}
|
| 429 |
type="text"
|
| 430 |
value={url}
|
| 431 |
disabled={isSearching}
|
|
|
|
| 463 |
>
|
| 464 |
<HeroInputSubmitButton
|
| 465 |
dirty={url.length > 0}
|
| 466 |
+
buttonText={mode === 'website' ? (isURL(url) ? 'Scrape Site' : 'Search') : 'Submit Paper'}
|
| 467 |
disabled={isSearching}
|
| 468 |
/>
|
| 469 |
</div>
|
|
|
|
| 471 |
)}
|
| 472 |
</div>
|
| 473 |
|
| 474 |
+
{/* Options Section - Only show when valid URL and in website mode */}
|
| 475 |
<div className={`overflow-hidden transition-all duration-500 ease-in-out ${
|
| 476 |
+
mode === 'website' && isValidUrl ? (extendBrandStyles ? 'max-h-[400px]' : 'max-h-[300px]') + ' opacity-100' : 'max-h-0 opacity-0'
|
| 477 |
}`}>
|
| 478 |
<div className="px-[28px] pt-0 pb-[28px]">
|
| 479 |
<div className="border-t border-gray-100 bg-white">
|
lib/arxiv.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import axios from 'axios';
|
| 2 |
+
import { XMLParser } from 'fast-xml-parser';
|
| 3 |
+
|
| 4 |
+
export interface ArxivPaper {
|
| 5 |
+
id: string;
|
| 6 |
+
title: string;
|
| 7 |
+
summary: string;
|
| 8 |
+
authors: string[];
|
| 9 |
+
published: string;
|
| 10 |
+
updated: string;
|
| 11 |
+
link: string;
|
| 12 |
+
pdfUrl: string;
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
const parser = new XMLParser();
|
| 16 |
+
|
| 17 |
+
/**
|
| 18 |
+
* Fetches paper metadata from Arxiv API.
|
| 19 |
+
* Supports both direct Arxiv IDs and search queries.
|
| 20 |
+
*/
|
| 21 |
+
export async function fetchArxivPaper(query: string): Promise<ArxivPaper | null> {
|
| 22 |
+
try {
|
| 23 |
+
let id = '';
|
| 24 |
+
|
| 25 |
+
// Extract ID if it's a URL
|
| 26 |
+
if (query.includes('arxiv.org/abs/')) {
|
| 27 |
+
id = query.split('arxiv.org/abs/')[1].split('?')[0];
|
| 28 |
+
} else if (query.includes('arxiv.org/pdf/')) {
|
| 29 |
+
id = query.split('arxiv.org/pdf/')[1].replace('.pdf', '').split('?')[0];
|
| 30 |
+
} else if (/^\d{4}\.\d{4,5}$/.test(query)) {
|
| 31 |
+
id = query;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
const apiUrl = id
|
| 35 |
+
? `http://export.arxiv.org/api/query?id_list=${id}`
|
| 36 |
+
: `http://export.arxiv.org/api/query?search_query=all:${encodeURIComponent(query)}&max_results=1`;
|
| 37 |
+
|
| 38 |
+
console.log(`[arxiv] Fetching: ${apiUrl}`);
|
| 39 |
+
const response = await axios.get(apiUrl);
|
| 40 |
+
const jsonObj = parser.parse(response.data);
|
| 41 |
+
|
| 42 |
+
const entry = jsonObj.feed?.entry;
|
| 43 |
+
if (!entry) return null;
|
| 44 |
+
|
| 45 |
+
const paperEntry = Array.isArray(entry) ? entry[0] : entry;
|
| 46 |
+
|
| 47 |
+
return {
|
| 48 |
+
id: paperEntry.id,
|
| 49 |
+
title: paperEntry.title.trim().replace(/\n/g, ' '),
|
| 50 |
+
summary: paperEntry.summary.trim(),
|
| 51 |
+
authors: Array.isArray(paperEntry.author)
|
| 52 |
+
? paperEntry.author.map((a: any) => a.name)
|
| 53 |
+
: [paperEntry.author.name],
|
| 54 |
+
published: paperEntry.published,
|
| 55 |
+
updated: paperEntry.updated,
|
| 56 |
+
link: paperEntry.id,
|
| 57 |
+
pdfUrl: paperEntry.id.replace('abs', 'pdf') + '.pdf'
|
| 58 |
+
};
|
| 59 |
+
} catch (error) {
|
| 60 |
+
console.error('[arxiv] Error fetching paper:', error);
|
| 61 |
+
return null;
|
| 62 |
+
}
|
| 63 |
+
}
|
lib/sandbox/providers/e2b-provider.ts
CHANGED
|
@@ -228,6 +228,47 @@ export class E2BProvider extends SandboxProvider {
|
|
| 228 |
};
|
| 229 |
}
|
| 230 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
async setupViteApp(): Promise<void> {
|
| 232 |
if (!this.sandbox) {
|
| 233 |
throw new Error('No active sandbox');
|
|
|
|
| 228 |
};
|
| 229 |
}
|
| 230 |
|
| 231 |
+
async setupPythonEnv(): Promise<void> {
|
| 232 |
+
if (!this.sandbox) {
|
| 233 |
+
throw new Error('No active sandbox');
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
console.log('[E2BProvider] Setting up Python environment...');
|
| 237 |
+
|
| 238 |
+
// Create directory structure
|
| 239 |
+
await this.sandbox.runCode(`
|
| 240 |
+
import os
|
| 241 |
+
os.makedirs('/home/user/app', exist_ok=True)
|
| 242 |
+
print('✓ /home/user/app created')
|
| 243 |
+
`);
|
| 244 |
+
|
| 245 |
+
// Create a requirements.txt with common scientific libraries
|
| 246 |
+
const requirements = `
|
| 247 |
+
numpy
|
| 248 |
+
pandas
|
| 249 |
+
matplotlib
|
| 250 |
+
scipy
|
| 251 |
+
scikit-learn
|
| 252 |
+
requests
|
| 253 |
+
`.trim();
|
| 254 |
+
|
| 255 |
+
await this.writeFile('requirements.txt', requirements);
|
| 256 |
+
|
| 257 |
+
// Create a main.py
|
| 258 |
+
const mainPy = `
|
| 259 |
+
def main():
|
| 260 |
+
print("Hello from Paper2Code Python Environment!")
|
| 261 |
+
|
| 262 |
+
if __name__ == "__main__":
|
| 263 |
+
main()
|
| 264 |
+
`.trim();
|
| 265 |
+
|
| 266 |
+
await this.writeFile('main.py', mainPy);
|
| 267 |
+
|
| 268 |
+
// Install requirements
|
| 269 |
+
await this.runCommand('pip install -r requirements.txt');
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
async setupViteApp(): Promise<void> {
|
| 273 |
if (!this.sandbox) {
|
| 274 |
throw new Error('No active sandbox');
|
lib/sandbox/types.ts
CHANGED
|
@@ -58,6 +58,11 @@ export abstract class SandboxProvider {
|
|
| 58 |
throw new Error('setupViteApp not implemented for this provider');
|
| 59 |
}
|
| 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
async restartViteServer(): Promise<void> {
|
| 62 |
// Default implementation for restarting Vite
|
| 63 |
throw new Error('restartViteServer not implemented for this provider');
|
|
|
|
| 58 |
throw new Error('setupViteApp not implemented for this provider');
|
| 59 |
}
|
| 60 |
|
| 61 |
+
async setupPythonEnv(): Promise<void> {
|
| 62 |
+
// Default implementation for setting up a Python environment
|
| 63 |
+
throw new Error('setupPythonEnv not implemented for this provider');
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
async restartViteServer(): Promise<void> {
|
| 67 |
// Default implementation for restarting Vite
|
| 68 |
throw new Error('restartViteServer not implemented for this provider');
|
package.json
CHANGED
|
@@ -53,12 +53,14 @@
|
|
| 53 |
"@vercel/sandbox": "^0.0.17",
|
| 54 |
"ai": "^5.0.0",
|
| 55 |
"autoprefixer": "^10.4.21",
|
|
|
|
| 56 |
"class-variance-authority": "^0.7.1",
|
| 57 |
"classnames": "^2.5.1",
|
| 58 |
"clsx": "^2.1.1",
|
| 59 |
"copy-to-clipboard": "^3.3.3",
|
| 60 |
"cors": "^2.8.5",
|
| 61 |
"dotenv": "^17.2.1",
|
|
|
|
| 62 |
"framer-motion": "^12.23.12",
|
| 63 |
"groq-sdk": "^0.29.0",
|
| 64 |
"jotai": "^2.14.0",
|
|
|
|
| 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",
|
pnpm-lock.yaml
CHANGED
|
@@ -128,6 +128,9 @@ importers:
|
|
| 128 |
autoprefixer:
|
| 129 |
specifier: ^10.4.21
|
| 130 |
version: 10.4.21(postcss@8.5.6)
|
|
|
|
|
|
|
|
|
|
| 131 |
class-variance-authority:
|
| 132 |
specifier: ^0.7.1
|
| 133 |
version: 0.7.1
|
|
@@ -146,6 +149,9 @@ importers:
|
|
| 146 |
dotenv:
|
| 147 |
specifier: ^17.2.1
|
| 148 |
version: 17.2.1
|
|
|
|
|
|
|
|
|
|
| 149 |
framer-motion:
|
| 150 |
specifier: ^12.23.12
|
| 151 |
version: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
@@ -1763,8 +1769,8 @@ packages:
|
|
| 1763 |
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
|
| 1764 |
engines: {node: '>=4'}
|
| 1765 |
|
| 1766 |
-
axios@1.
|
| 1767 |
-
resolution: {integrity: sha512-
|
| 1768 |
|
| 1769 |
axobject-query@4.1.0:
|
| 1770 |
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
|
|
@@ -2195,6 +2201,10 @@ packages:
|
|
| 2195 |
fast-levenshtein@2.0.6:
|
| 2196 |
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
| 2197 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2198 |
fastq@1.19.1:
|
| 2199 |
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
| 2200 |
|
|
@@ -2252,6 +2262,10 @@ packages:
|
|
| 2252 |
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
|
| 2253 |
engines: {node: '>= 6'}
|
| 2254 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2255 |
format@0.2.2:
|
| 2256 |
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
|
| 2257 |
engines: {node: '>=0.4.x'}
|
|
@@ -3278,6 +3292,9 @@ packages:
|
|
| 3278 |
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
| 3279 |
engines: {node: '>=8'}
|
| 3280 |
|
|
|
|
|
|
|
|
|
|
| 3281 |
styled-jsx@5.1.6:
|
| 3282 |
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
|
| 3283 |
engines: {node: '>= 12.0.0'}
|
|
@@ -3829,7 +3846,7 @@ snapshots:
|
|
| 3829 |
|
| 3830 |
'@mendable/firecrawl-js@4.3.4':
|
| 3831 |
dependencies:
|
| 3832 |
-
axios: 1.
|
| 3833 |
typescript-event-target: 1.1.1
|
| 3834 |
zod: 3.25.76
|
| 3835 |
zod-to-json-schema: 3.24.6(zod@3.25.76)
|
|
@@ -5116,10 +5133,10 @@ snapshots:
|
|
| 5116 |
|
| 5117 |
axe-core@4.10.3: {}
|
| 5118 |
|
| 5119 |
-
axios@1.
|
| 5120 |
dependencies:
|
| 5121 |
follow-redirects: 1.15.11
|
| 5122 |
-
form-data: 4.0.
|
| 5123 |
proxy-from-env: 1.1.0
|
| 5124 |
transitivePeerDependencies:
|
| 5125 |
- debug
|
|
@@ -5692,6 +5709,10 @@ snapshots:
|
|
| 5692 |
|
| 5693 |
fast-levenshtein@2.0.6: {}
|
| 5694 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5695 |
fastq@1.19.1:
|
| 5696 |
dependencies:
|
| 5697 |
reusify: 1.1.0
|
|
@@ -5745,6 +5766,14 @@ snapshots:
|
|
| 5745 |
hasown: 2.0.2
|
| 5746 |
mime-types: 2.1.35
|
| 5747 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5748 |
format@0.2.2: {}
|
| 5749 |
|
| 5750 |
formdata-node@4.4.1:
|
|
@@ -6836,6 +6865,8 @@ snapshots:
|
|
| 6836 |
|
| 6837 |
strip-json-comments@3.1.1: {}
|
| 6838 |
|
|
|
|
|
|
|
| 6839 |
styled-jsx@5.1.6(react@19.1.0):
|
| 6840 |
dependencies:
|
| 6841 |
client-only: 0.0.1
|
|
|
|
| 128 |
autoprefixer:
|
| 129 |
specifier: ^10.4.21
|
| 130 |
version: 10.4.21(postcss@8.5.6)
|
| 131 |
+
axios:
|
| 132 |
+
specifier: ^1.13.5
|
| 133 |
+
version: 1.13.5
|
| 134 |
class-variance-authority:
|
| 135 |
specifier: ^0.7.1
|
| 136 |
version: 0.7.1
|
|
|
|
| 149 |
dotenv:
|
| 150 |
specifier: ^17.2.1
|
| 151 |
version: 17.2.1
|
| 152 |
+
fast-xml-parser:
|
| 153 |
+
specifier: ^5.3.6
|
| 154 |
+
version: 5.3.6
|
| 155 |
framer-motion:
|
| 156 |
specifier: ^12.23.12
|
| 157 |
version: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
|
|
|
| 1769 |
resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
|
| 1770 |
engines: {node: '>=4'}
|
| 1771 |
|
| 1772 |
+
axios@1.13.5:
|
| 1773 |
+
resolution: {integrity: sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==}
|
| 1774 |
|
| 1775 |
axobject-query@4.1.0:
|
| 1776 |
resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
|
|
|
|
| 2201 |
fast-levenshtein@2.0.6:
|
| 2202 |
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
| 2203 |
|
| 2204 |
+
fast-xml-parser@5.3.6:
|
| 2205 |
+
resolution: {integrity: sha512-QNI3sAvSvaOiaMl8FYU4trnEzCwiRr8XMWgAHzlrWpTSj+QaCSvOf1h82OEP1s4hiAXhnbXSyFWCf4ldZzZRVA==}
|
| 2206 |
+
hasBin: true
|
| 2207 |
+
|
| 2208 |
fastq@1.19.1:
|
| 2209 |
resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
|
| 2210 |
|
|
|
|
| 2262 |
resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
|
| 2263 |
engines: {node: '>= 6'}
|
| 2264 |
|
| 2265 |
+
form-data@4.0.5:
|
| 2266 |
+
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
| 2267 |
+
engines: {node: '>= 6'}
|
| 2268 |
+
|
| 2269 |
format@0.2.2:
|
| 2270 |
resolution: {integrity: sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==}
|
| 2271 |
engines: {node: '>=0.4.x'}
|
|
|
|
| 3292 |
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
| 3293 |
engines: {node: '>=8'}
|
| 3294 |
|
| 3295 |
+
strnum@2.1.2:
|
| 3296 |
+
resolution: {integrity: sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==}
|
| 3297 |
+
|
| 3298 |
styled-jsx@5.1.6:
|
| 3299 |
resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
|
| 3300 |
engines: {node: '>= 12.0.0'}
|
|
|
|
| 3846 |
|
| 3847 |
'@mendable/firecrawl-js@4.3.4':
|
| 3848 |
dependencies:
|
| 3849 |
+
axios: 1.13.5
|
| 3850 |
typescript-event-target: 1.1.1
|
| 3851 |
zod: 3.25.76
|
| 3852 |
zod-to-json-schema: 3.24.6(zod@3.25.76)
|
|
|
|
| 5133 |
|
| 5134 |
axe-core@4.10.3: {}
|
| 5135 |
|
| 5136 |
+
axios@1.13.5:
|
| 5137 |
dependencies:
|
| 5138 |
follow-redirects: 1.15.11
|
| 5139 |
+
form-data: 4.0.5
|
| 5140 |
proxy-from-env: 1.1.0
|
| 5141 |
transitivePeerDependencies:
|
| 5142 |
- debug
|
|
|
|
| 5709 |
|
| 5710 |
fast-levenshtein@2.0.6: {}
|
| 5711 |
|
| 5712 |
+
fast-xml-parser@5.3.6:
|
| 5713 |
+
dependencies:
|
| 5714 |
+
strnum: 2.1.2
|
| 5715 |
+
|
| 5716 |
fastq@1.19.1:
|
| 5717 |
dependencies:
|
| 5718 |
reusify: 1.1.0
|
|
|
|
| 5766 |
hasown: 2.0.2
|
| 5767 |
mime-types: 2.1.35
|
| 5768 |
|
| 5769 |
+
form-data@4.0.5:
|
| 5770 |
+
dependencies:
|
| 5771 |
+
asynckit: 0.4.0
|
| 5772 |
+
combined-stream: 1.0.8
|
| 5773 |
+
es-set-tostringtag: 2.1.0
|
| 5774 |
+
hasown: 2.0.2
|
| 5775 |
+
mime-types: 2.1.35
|
| 5776 |
+
|
| 5777 |
format@0.2.2: {}
|
| 5778 |
|
| 5779 |
formdata-node@4.4.1:
|
|
|
|
| 6865 |
|
| 6866 |
strip-json-comments@3.1.1: {}
|
| 6867 |
|
| 6868 |
+
strnum@2.1.2: {}
|
| 6869 |
+
|
| 6870 |
styled-jsx@5.1.6(react@19.1.0):
|
| 6871 |
dependencies:
|
| 6872 |
client-only: 0.0.1
|