+
+ {/* Close the whole panel */}
+
-
-
(askComponentRef.current = ref)}
- />
+
+ {/* Split layout: conversation on the left, code viewer on the right.
+ When no codemap citation needs a viewer, the conversation uses the
+ full width (same as fast / deep-research modes). ~6:4 when open. */}
+
+
+
(askComponentRef.current = ref)}
+ onOpenCodeViewer={openCodeViewer}
+ onCloseCodeViewer={() => setCodeViewerOpen(false)}
+ />
+
+ {codeViewerOpen && (
+
+
+ setCodeViewerTarget({ file_path: f, start_line: null, end_line: null, snippet: '' })
+ }
+ />
+
+ )}
diff --git a/src/app/[owner]/[repo]/workshop/page.tsx b/src/app/[owner]/[repo]/workshop/page.tsx
index 7a1e29c75..83abe29c8 100644
--- a/src/app/[owner]/[repo]/workshop/page.tsx
+++ b/src/app/[owner]/[repo]/workshop/page.tsx
@@ -301,7 +301,7 @@ Make the workshop content in ${language === 'en' ? 'English' :
language === 'zh-tw' ? 'Traditional Chinese (繁體中文)' :
language === 'es' ? 'Spanish (Español)' :
language === 'kr' ? 'Korean (한국어)' :
- language === 'vi' ? 'Vietnamese (Tiếng Việt)' :
+ language === 'vi' ? 'Vietnamese (Tiếng Việt)' :
language === "pt-br" ? "Brazilian Portuguese (Português Brasileiro)" :
language === "fr" ? "Français (French)" :
language === "ru" ? "Русский (Russian)" :
diff --git a/src/app/api/auth/status/route.ts b/src/app/api/auth/status/route.ts
index 601bb30b2..5bb7c6666 100644
--- a/src/app/api/auth/status/route.ts
+++ b/src/app/api/auth/status/route.ts
@@ -11,14 +11,14 @@ export async function GET() {
'Content-Type': 'application/json',
},
});
-
+
if (!response.ok) {
return NextResponse.json(
{ error: `Backend server returned ${response.status}` },
{ status: response.status }
);
}
-
+
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
diff --git a/src/app/api/auth/validate/route.ts b/src/app/api/auth/validate/route.ts
index 0a9b7c0a8..8c82c928b 100644
--- a/src/app/api/auth/validate/route.ts
+++ b/src/app/api/auth/validate/route.ts
@@ -5,7 +5,7 @@ const TARGET_SERVER_BASE_URL = process.env.SERVER_BASE_URL || 'http://localhost:
export async function POST(request: NextRequest) {
try {
const body = await request.json();
-
+
// Forward the request to the backend API
const response = await fetch(`${TARGET_SERVER_BASE_URL}/auth/validate`, {
method: 'POST',
@@ -14,14 +14,14 @@ export async function POST(request: NextRequest) {
},
body: JSON.stringify(body),
});
-
+
if (!response.ok) {
return NextResponse.json(
{ error: `Backend server returned ${response.status}` },
{ status: response.status }
);
}
-
+
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
diff --git a/src/app/api/chat/stream/route.ts b/src/app/api/chat/stream/route.ts
index 544c5d1eb..9d3afb3ae 100644
--- a/src/app/api/chat/stream/route.ts
+++ b/src/app/api/chat/stream/route.ts
@@ -110,4 +110,4 @@ export async function OPTIONS() {
'Access-Control-Allow-Headers': 'Content-Type, Authorization', // Adjust as per client's request headers
},
});
-}
\ No newline at end of file
+}
diff --git a/src/app/api/models/config/route.ts b/src/app/api/models/config/route.ts
index 802df2217..01a57c793 100644
--- a/src/app/api/models/config/route.ts
+++ b/src/app/api/models/config/route.ts
@@ -27,7 +27,7 @@ export async function GET() {
const modelConfig = await backendResponse.json();
return NextResponse.json(modelConfig);
} catch (error) {
- console.error('Error fetching model configurations:', error);
+ console.error('Error fetching model configurations:', error);
return new NextResponse(JSON.stringify({ error: error }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
diff --git a/src/app/api/repo/prepare/route.ts b/src/app/api/repo/prepare/route.ts
new file mode 100644
index 000000000..42ad43f89
--- /dev/null
+++ b/src/app/api/repo/prepare/route.ts
@@ -0,0 +1,74 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+// Proxy for the backend's repository-indexing endpoint.
+//
+// The backend streams Server-Sent Events (a heartbeat/progress line at least
+// every ~10s), so this route just pipes the byte stream straight through. No
+// custom fetch timeout is needed: the first byte (headers) arrives immediately
+// and heartbeats keep the body well within undici's default 300s timeouts.
+export const runtime = 'nodejs';
+export const dynamic = 'force-dynamic';
+
+const TARGET_SERVER_BASE_URL = process.env.SERVER_BASE_URL || 'http://localhost:8001';
+
+export async function POST(req: NextRequest) {
+ try {
+ const requestBody = await req.json();
+ const targetUrl = `${TARGET_SERVER_BASE_URL}/repo/prepare`;
+
+ const backendResponse = await fetch(targetUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ Accept: 'text/event-stream',
+ },
+ body: JSON.stringify(requestBody),
+ });
+
+ if (!backendResponse.ok || !backendResponse.body) {
+ const errorBody = await backendResponse.text().catch(() => '');
+ return new NextResponse(errorBody || 'Failed to start repository indexing', {
+ status: backendResponse.status || 500,
+ });
+ }
+
+ // Pipe the SSE stream from the backend to the client.
+ const stream = new ReadableStream({
+ async start(controller) {
+ const reader = backendResponse.body!.getReader();
+ try {
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ controller.enqueue(value);
+ }
+ } catch (error) {
+ console.error('Error reading from backend prepare stream in proxy:', error);
+ controller.error(error);
+ } finally {
+ controller.close();
+ reader.releaseLock();
+ }
+ },
+ cancel(reason) {
+ console.log('Client cancelled repo prepare stream:', reason);
+ },
+ });
+
+ return new NextResponse(stream, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'text/event-stream',
+ 'Cache-Control': 'no-cache, no-transform',
+ 'X-Accel-Buffering': 'no',
+ },
+ });
+ } catch (error) {
+ console.error('Error in API proxy route (/api/repo/prepare):', error);
+ const errorMessage = error instanceof Error ? error.message : 'Internal Server Error in proxy';
+ return new NextResponse(JSON.stringify({ error: errorMessage }), {
+ status: 500,
+ headers: { 'Content-Type': 'application/json' },
+ });
+ }
+}
diff --git a/src/app/api/wiki/projects/route.ts b/src/app/api/wiki/projects/route.ts
index f87ed35d9..1c2d3d948 100644
--- a/src/app/api/wiki/projects/route.ts
+++ b/src/app/api/wiki/projects/route.ts
@@ -101,4 +101,4 @@ export async function DELETE(request: Request) {
const message = error instanceof Error ? error.message : 'An unknown error occurred';
return NextResponse.json({ error: `Failed to delete project: ${message}` }, { status: 500 });
}
-}
\ No newline at end of file
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index 9e05a2ef9..5be90c925 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -622,4 +622,4 @@ export default function Home() {
);
-}
\ No newline at end of file
+}
diff --git a/src/app/wiki/projects/page.tsx b/src/app/wiki/projects/page.tsx
index 751286c8d..75522ecba 100644
--- a/src/app/wiki/projects/page.tsx
+++ b/src/app/wiki/projects/page.tsx
@@ -16,4 +16,4 @@ export default function WikiProjectsPage() {
/>