diff --git a/surfsense_web/components/editor-panel/editor-panel.tsx b/surfsense_web/components/editor-panel/editor-panel.tsx
index 308ad158b..eb55b583e 100644
--- a/surfsense_web/components/editor-panel/editor-panel.tsx
+++ b/surfsense_web/components/editor-panel/editor-panel.tsx
@@ -1,17 +1,23 @@
"use client";
+import dynamic from "next/dynamic";
import { useAtomValue, useSetAtom } from "jotai";
import { AlertCircle, XIcon } from "lucide-react";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "sonner";
import { closeEditorPanelAtom, editorPanelAtom } from "@/atoms/editor/editor-panel.atom";
-import { PlateEditor } from "@/components/editor/plate-editor";
import { MarkdownViewer } from "@/components/markdown-viewer";
import { Button } from "@/components/ui/button";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
+import { Skeleton } from "@/components/ui/skeleton";
import { useMediaQuery } from "@/hooks/use-media-query";
import { authenticatedFetch, getBearerToken, redirectToLogin } from "@/lib/auth-utils";
+const PlateEditor = dynamic(
+ () => import("@/components/editor/plate-editor").then((m) => ({ default: m.PlateEditor })),
+ { ssr: false, loading: () => }
+);
+
interface EditorContent {
document_id: number;
title: string;
diff --git a/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx b/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx
index a75752217..99f17f976 100644
--- a/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx
+++ b/surfsense_web/components/hitl-edit-panel/hitl-edit-panel.tsx
@@ -1,5 +1,6 @@
"use client";
+import dynamic from "next/dynamic";
import { format } from "date-fns";
import { TagInput, type Tag as TagType } from "emblor";
import { useAtomValue, useSetAtom } from "jotai";
@@ -7,16 +8,21 @@ import { CalendarIcon, XIcon } from "lucide-react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import type { ExtraField } from "@/atoms/chat/hitl-edit-panel.atom";
import { closeHitlEditPanelAtom, hitlEditPanelAtom } from "@/atoms/chat/hitl-edit-panel.atom";
-import { PlateEditor } from "@/components/editor/plate-editor";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import { Drawer, DrawerContent, DrawerHandle, DrawerTitle } from "@/components/ui/drawer";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
+import { Skeleton } from "@/components/ui/skeleton";
import { Textarea } from "@/components/ui/textarea";
import { useMediaQuery } from "@/hooks/use-media-query";
+const PlateEditor = dynamic(
+ () => import("@/components/editor/plate-editor").then((m) => ({ default: m.PlateEditor })),
+ { ssr: false, loading: () => }
+);
+
function parseEmailsToTags(value: string): TagType[] {
if (!value.trim()) return [];
return value
diff --git a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx
index abc73425e..d03a8b5d7 100644
--- a/surfsense_web/components/layout/providers/LayoutDataProvider.tsx
+++ b/surfsense_web/components/layout/providers/LayoutDataProvider.tsx
@@ -110,9 +110,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
const resetTabs = useSetAtom(resetTabsAtom);
const removeChatTab = useSetAtom(removeChatTabAtom);
- // State for handling new chat navigation when router is out of sync
- const [pendingNewChat, setPendingNewChat] = useState(false);
-
// Key used to force-remount the page component (e.g. after deleting the active chat
// when the router is out of sync due to replaceState)
const [chatResetKey, setChatResetKey] = useState(0);
@@ -262,17 +259,6 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
const [isDeletingSearchSpace, setIsDeletingSearchSpace] = useState(false);
const [isLeavingSearchSpace, setIsLeavingSearchSpace] = useState(false);
- // Effect to complete new chat navigation after router syncs
- // This runs when handleNewChat detected an out-of-sync state and triggered a sync
- useEffect(() => {
- if (pendingNewChat && params?.chat_id) {
- // Router is now synced (chat_id is in params), complete navigation to new-chat
- resetCurrentThread();
- router.push(`/dashboard/${searchSpaceId}/new-chat`);
- setPendingNewChat(false);
- }
- }, [pendingNewChat, params?.chat_id, router, searchSpaceId, resetCurrentThread]);
-
// Reset transient slide-out panels and tabs when switching search spaces.
// Use a ref to skip the initial mount — only reset when the space actually changes.
const prevSearchSpaceIdRef = useRef(searchSpaceId);
@@ -555,14 +541,17 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid
if (isOutOfSync) {
// First sync Next.js router by navigating to the current chat's actual URL
// This updates the router's internal state to match the browser URL
+ resetCurrentThread();
router.replace(`/dashboard/${searchSpaceId}/new-chat/${currentThreadState.id}`);
- // Set flag to trigger navigation to new-chat after params update
- setPendingNewChat(true);
+ // Allow router to sync, then navigate to fresh new-chat
+ setTimeout(() => {
+ router.push(`/dashboard/${searchSpaceId}/new-chat`);
+ }, 0);
} else {
// Normal navigation - router is in sync
router.push(`/dashboard/${searchSpaceId}/new-chat`);
}
- }, [router, searchSpaceId, currentThreadState.id, params?.chat_id]);
+ }, [router, searchSpaceId, currentThreadState.id, params?.chat_id, resetCurrentThread]);
const handleChatSelect = useCallback(
(chat: ChatItem) => {
@@ -848,7 +837,7 @@ export function LayoutDataProvider({ searchSpaceId, children }: LayoutDataProvid