From b54ce1b87ed8047f4b9be4bb6758ecb1bc1ea674 Mon Sep 17 00:00:00 2001 From: Sma1lboy <541898146chen@gmail.com> Date: Sun, 19 Oct 2025 00:16:14 -0700 Subject: [PATCH 1/6] feat: Integrate Sandpack code editor into RightPanel; add file management and editing capabilities with project structure visibility --- app/api/project/file/route.ts | 43 + app/api/project/files/route.ts | 35 + app/layout.tsx | 11 +- bun.lock | 103 +- components/preview/code-editor/index.ts | 3 + .../preview/code-editor/sandpack-editor.tsx | 298 ++++ components/preview/code-editor/types.ts | 11 + components/preview/right-panel.tsx | 9 +- components/providers.tsx | 28 + docs/2025-10-18/code_editor_integration.md | 410 ++++++ .../code_editor_solutions_comparison_cn.md | 1206 +++++++++++++++++ package.json | 1 + 12 files changed, 2147 insertions(+), 11 deletions(-) create mode 100644 app/api/project/file/route.ts create mode 100644 app/api/project/files/route.ts create mode 100644 components/preview/code-editor/index.ts create mode 100644 components/preview/code-editor/sandpack-editor.tsx create mode 100644 components/preview/code-editor/types.ts create mode 100644 components/providers.tsx create mode 100644 docs/2025-10-18/code_editor_integration.md create mode 100644 docs/2025-10-18/code_editor_solutions_comparison_cn.md diff --git a/app/api/project/file/route.ts b/app/api/project/file/route.ts new file mode 100644 index 0000000..64a6018 --- /dev/null +++ b/app/api/project/file/route.ts @@ -0,0 +1,43 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { ProjectManager } from '@/lib/project-manager'; + +const projectManager = ProjectManager.getInstance(); + +export async function GET(req: NextRequest) { + try { + const { searchParams } = new URL(req.url); + const projectId = searchParams.get('projectId'); + const filePath = searchParams.get('filePath'); + + if (!projectId || typeof projectId !== 'string') { + return NextResponse.json( + { success: false, error: 'projectId is required' }, + { status: 400 } + ); + } + + if (!filePath || typeof filePath !== 'string') { + return NextResponse.json( + { success: false, error: 'filePath is required' }, + { status: 400 } + ); + } + + const content = await projectManager.readFile(projectId, filePath); + + return NextResponse.json({ + success: true, + content + }); + } catch (error) { + console.error('[api/project/file] Error reading file:', error); + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + { status: 500 } + ); + } +} + diff --git a/app/api/project/files/route.ts b/app/api/project/files/route.ts new file mode 100644 index 0000000..ae21b5a --- /dev/null +++ b/app/api/project/files/route.ts @@ -0,0 +1,35 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { ProjectManager } from '@/lib/project-manager'; + +const projectManager = ProjectManager.getInstance(); + +export async function GET(req: NextRequest) { + try { + const { searchParams } = new URL(req.url); + const projectId = searchParams.get('projectId'); + + if (!projectId || typeof projectId !== 'string') { + return NextResponse.json( + { success: false, error: 'projectId is required' }, + { status: 400 } + ); + } + + const files = await projectManager.listFiles(projectId); + + return NextResponse.json({ + success: true, + files + }); + } catch (error) { + console.error('[api/project/files] Error listing files:', error); + return NextResponse.json( + { + success: false, + error: error instanceof Error ? error.message : 'Unknown error', + }, + { status: 500 } + ); + } +} + diff --git a/app/layout.tsx b/app/layout.tsx index 44a9227..d9e1dd7 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,7 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import { Toaster } from "@/components/ui/sonner"; -import { ThemeProvider } from "@/components/theme-provider"; +import { Providers } from "@/components/providers"; import "./globals.css"; const geistSans = Geist({ @@ -29,15 +29,10 @@ export default function RootLayout({
-