Merge remote-tracking branch 'upstream/dev' into impr/thinking-steps

This commit is contained in:
Anish Sarkar 2026-03-25 01:50:10 +05:30
commit 778cfac6fa
96 changed files with 4065 additions and 3274 deletions

View file

@ -0,0 +1,5 @@
import { NextResponse } from "next/server";
export async function POST() {
return NextResponse.json([]);
}

View file

@ -0,0 +1,50 @@
import { mustGetQuery } from "@rocicorp/zero";
import { handleQueryRequest } from "@rocicorp/zero/server";
import { NextResponse } from "next/server";
import type { Context } from "@/types/zero";
import { queries } from "@/zero/queries";
import { schema } from "@/zero/schema";
const backendURL = process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
async function authenticateRequest(
request: Request
): Promise<{ ctx: Context; error?: never } | { ctx?: never; error: NextResponse }> {
const authHeader = request.headers.get("Authorization");
if (!authHeader?.startsWith("Bearer ")) {
return { ctx: undefined };
}
try {
const res = await fetch(`${backendURL}/users/me`, {
headers: { Authorization: authHeader },
});
if (!res.ok) {
return { error: NextResponse.json({ error: "Unauthorized" }, { status: 401 }) };
}
const user = await res.json();
return { ctx: { userId: String(user.id) } };
} catch {
return { error: NextResponse.json({ error: "Auth service unavailable" }, { status: 503 }) };
}
}
export async function POST(request: Request) {
const auth = await authenticateRequest(request);
if (auth.error) {
return auth.error;
}
const result = await handleQueryRequest(
(name, args) => {
const query = mustGetQuery(queries, name);
return query.fn({ args, ctx: auth.ctx });
},
schema,
request
);
return NextResponse.json(result);
}

View file

@ -40,7 +40,7 @@ import { MobileHitlEditPanel } from "@/components/hitl-edit-panel/hitl-edit-pane
import { MobileReportPanel } from "@/components/report-panel/report-panel";
import { Skeleton } from "@/components/ui/skeleton";
import { useChatSessionStateSync } from "@/hooks/use-chat-session-state";
import { useMessagesElectric } from "@/hooks/use-messages-electric";
import { useMessagesSync } from "@/hooks/use-messages-sync";
import { documentsApiService } from "@/lib/apis/documents-api.service";
import { getBearerToken } from "@/lib/auth-utils";
import { convertToThreadMessage } from "@/lib/chat/message-utils";
@ -192,13 +192,13 @@ export default function NewChatPage() {
// Get current user for author info in shared chats
const { data: currentUser } = useAtomValue(currentUserAtom);
// Live collaboration: sync session state and messages via Electric SQL
// Live collaboration: sync session state and messages via Zero
useChatSessionStateSync(threadId);
const { data: membersData } = useAtomValue(membersAtom);
const handleElectricMessagesUpdate = useCallback(
const handleSyncedMessagesUpdate = useCallback(
(
electricMessages: {
syncedMessages: {
id: number;
thread_id: number;
role: string;
@ -212,11 +212,11 @@ export default function NewChatPage() {
}
setMessages((prev) => {
if (electricMessages.length < prev.length) {
if (syncedMessages.length < prev.length) {
return prev;
}
return electricMessages.map((msg) => {
return syncedMessages.map((msg) => {
const member = msg.author_id
? membersData?.find((m) => m.user_id === msg.author_id)
: null;
@ -243,7 +243,7 @@ export default function NewChatPage() {
[isRunning, membersData]
);
useMessagesElectric(threadId, handleElectricMessagesUpdate);
useMessagesSync(threadId, handleSyncedMessagesUpdate);
// Extract search_space_id from URL params
const searchSpaceId = useMemo(() => {
@ -266,6 +266,7 @@ export default function NewChatPage() {
// Initialize thread and load messages
// For new chats (no urlChatId), we use lazy creation - thread is created on first message
// biome-ignore lint/correctness/useExhaustiveDependencies: searchSpaceId triggers re-init when switching spaces with the same urlChatId
const initializeThread = useCallback(async () => {
setIsInitializing(true);

View file

@ -3,10 +3,10 @@ import "./globals.css";
import { RootProvider } from "fumadocs-ui/provider/next";
import { Roboto } from "next/font/google";
import { AnnouncementToastProvider } from "@/components/announcements/AnnouncementToastProvider";
import { ElectricProvider } from "@/components/providers/ElectricProvider";
import { GlobalLoadingProvider } from "@/components/providers/GlobalLoadingProvider";
import { I18nProvider } from "@/components/providers/I18nProvider";
import { PostHogProvider } from "@/components/providers/PostHogProvider";
import { ZeroProvider } from "@/components/providers/ZeroProvider";
import { ThemeProvider } from "@/components/theme/theme-provider";
import { Toaster } from "@/components/ui/sonner";
import { LocaleProvider } from "@/contexts/LocaleContext";
@ -141,9 +141,9 @@ export default function RootLayout({
>
<RootProvider>
<ReactQueryClientProvider>
<ElectricProvider>
<ZeroProvider>
<GlobalLoadingProvider>{children}</GlobalLoadingProvider>
</ElectricProvider>
</ZeroProvider>
</ReactQueryClientProvider>
<Toaster />
<AnnouncementToastProvider />

View file

@ -213,7 +213,7 @@ export default function sitemap(): MetadataRoute.Sitemap {
},
// How-to documentation
{
url: "https://www.surfsense.com/docs/how-to/electric-sql",
url: "https://www.surfsense.com/docs/how-to/zero-sync",
lastModified,
changeFrequency: "daily",
priority: 0.8,