diff --git a/surfsense_web/app/(home)/login/page.tsx b/surfsense_web/app/(home)/login/page.tsx index 161951d97..3dbbf21a9 100644 --- a/surfsense_web/app/(home)/login/page.tsx +++ b/surfsense_web/app/(home)/login/page.tsx @@ -1,8 +1,7 @@ "use client"; import { AnimatePresence, motion } from "motion/react"; -import { useRouter } from "next/navigation"; -import { useSearchParams } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { useTranslations } from "next-intl"; import { Suspense, useEffect, useState } from "react"; import { toast } from "sonner"; diff --git a/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx index 443c964cf..c5be2b590 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/logs/(manage)/page.tsx @@ -46,7 +46,6 @@ import { useParams } from "next/navigation"; import { useTranslations } from "next-intl"; import React, { useCallback, useContext, useEffect, useId, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; -import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { createLogMutationAtom, deleteLogMutationAtom, @@ -96,6 +95,7 @@ import { TableRow, } from "@/components/ui/table"; import type { CreateLogRequest, Log, UpdateLogRequest } from "@/contracts/types/log.types"; +import { useDebouncedValue } from "@/hooks/use-debounced-value"; import { type LogLevel, type LogStatus, useLogs, useLogsSummary } from "@/hooks/use-logs"; import { cn } from "@/lib/utils"; @@ -728,10 +728,7 @@ function LogsFilters({ setFilterInput(e.target.value)} placeholder={t("filter_by_message")} diff --git a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx index 080a36167..0b1369340 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/new-chat/[[...chat_id]]/page.tsx @@ -40,18 +40,62 @@ import { ThinkingStepsDataUI } from "@/components/assistant-ui/thinking-steps"; import { Thread } from "@/components/assistant-ui/thread"; import { useChatSessionStateSync } from "@/hooks/use-chat-session-state"; 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"; +import { + isPodcastGenerating, + looksLikePodcastRequest, + setActivePodcastTaskId, +} from "@/lib/chat/podcast-state"; +import { + addToolCall, + appendText, + buildContentForPersistence, + buildContentForUI, + type ContentPartsState, + FrameBatchedUpdater, + readSSEStream, + type ThinkingStepData, + updateThinkingSteps, + updateToolCall, +} from "@/lib/chat/streaming-state"; +import { + appendMessage, + createThread, + getRegenerateUrl, + getThreadFull, + getThreadMessages, + type ThreadRecord, +} from "@/lib/chat/thread-persistence"; +import { NotFoundError } from "@/lib/error"; +import { + trackChatCreated, + trackChatError, + trackChatMessageSent, + trackChatResponseReceived, +} from "@/lib/posthog/events"; import Loading from "../loading"; const MobileEditorPanel = dynamic( - () => import("@/components/editor-panel/editor-panel").then((m) => ({ default: m.MobileEditorPanel })), + () => + import("@/components/editor-panel/editor-panel").then((m) => ({ + default: m.MobileEditorPanel, + })), { ssr: false } ); const MobileHitlEditPanel = dynamic( - () => import("@/components/hitl-edit-panel/hitl-edit-panel").then((m) => ({ default: m.MobileHitlEditPanel })), + () => + import("@/components/hitl-edit-panel/hitl-edit-panel").then((m) => ({ + default: m.MobileHitlEditPanel, + })), { ssr: false } ); const MobileReportPanel = dynamic( - () => import("@/components/report-panel/report-panel").then((m) => ({ default: m.MobileReportPanel })), + () => + import("@/components/report-panel/report-panel").then((m) => ({ + default: m.MobileReportPanel, + })), { ssr: false } ); diff --git a/surfsense_web/app/desktop/suggestion/page.tsx b/surfsense_web/app/desktop/suggestion/page.tsx index 46e388568..d30da65f6 100644 --- a/surfsense_web/app/desktop/suggestion/page.tsx +++ b/surfsense_web/app/desktop/suggestion/page.tsx @@ -346,15 +346,11 @@ export default function SuggestionPage() { needsTruncation && !isExpanded ? option.slice(0, TRUNCATE_LENGTH) + "…" : option; return ( -
handleSelect(option)} - onKeyDown={(e) => { - if (e.key === "Enter") handleSelect(option); - }} > {index + 1} {displayText} @@ -370,7 +366,7 @@ export default function SuggestionPage() { {isExpanded ? "less" : "more"} )} -
+ ); })} diff --git a/surfsense_web/components/assistant-ui/assistant-message.tsx b/surfsense_web/components/assistant-ui/assistant-message.tsx index 626e6237f..097197107 100644 --- a/surfsense_web/components/assistant-ui/assistant-message.tsx +++ b/surfsense_web/components/assistant-ui/assistant-message.tsx @@ -51,131 +51,172 @@ const IS_QUICK_ASSIST_WINDOW = // Dynamically import tool UI components to avoid loading them in main bundle const GenerateReportToolUI = dynamic( - () => import("@/components/tool-ui/generate-report").then(m => ({ default: m.GenerateReportToolUI })), + () => + import("@/components/tool-ui/generate-report").then((m) => ({ + default: m.GenerateReportToolUI, + })), { ssr: false } ); const GeneratePodcastToolUI = dynamic( - () => import("@/components/tool-ui/generate-podcast").then(m => ({ default: m.GeneratePodcastToolUI })), + () => + import("@/components/tool-ui/generate-podcast").then((m) => ({ + default: m.GeneratePodcastToolUI, + })), { ssr: false } ); const GenerateVideoPresentationToolUI = dynamic( - () => import("@/components/tool-ui/video-presentation").then(m => ({ default: m.GenerateVideoPresentationToolUI })), + () => + import("@/components/tool-ui/video-presentation").then((m) => ({ + default: m.GenerateVideoPresentationToolUI, + })), { ssr: false } ); const GenerateImageToolUI = dynamic( - () => import("@/components/tool-ui/generate-image").then(m => ({ default: m.GenerateImageToolUI })), + () => + import("@/components/tool-ui/generate-image").then((m) => ({ default: m.GenerateImageToolUI })), { ssr: false } ); const SaveMemoryToolUI = dynamic( - () => import("@/components/tool-ui/user-memory").then(m => ({ default: m.SaveMemoryToolUI })), + () => import("@/components/tool-ui/user-memory").then((m) => ({ default: m.SaveMemoryToolUI })), { ssr: false } ); const RecallMemoryToolUI = dynamic( - () => import("@/components/tool-ui/user-memory").then(m => ({ default: m.RecallMemoryToolUI })), + () => import("@/components/tool-ui/user-memory").then((m) => ({ default: m.RecallMemoryToolUI })), { ssr: false } ); const SandboxExecuteToolUI = dynamic( - () => import("@/components/tool-ui/sandbox-execute").then(m => ({ default: m.SandboxExecuteToolUI })), + () => + import("@/components/tool-ui/sandbox-execute").then((m) => ({ + default: m.SandboxExecuteToolUI, + })), { ssr: false } ); const CreateNotionPageToolUI = dynamic( - () => import("@/components/tool-ui/notion").then(m => ({ default: m.CreateNotionPageToolUI })), + () => import("@/components/tool-ui/notion").then((m) => ({ default: m.CreateNotionPageToolUI })), { ssr: false } ); const UpdateNotionPageToolUI = dynamic( - () => import("@/components/tool-ui/notion").then(m => ({ default: m.UpdateNotionPageToolUI })), + () => import("@/components/tool-ui/notion").then((m) => ({ default: m.UpdateNotionPageToolUI })), { ssr: false } ); const DeleteNotionPageToolUI = dynamic( - () => import("@/components/tool-ui/notion").then(m => ({ default: m.DeleteNotionPageToolUI })), + () => import("@/components/tool-ui/notion").then((m) => ({ default: m.DeleteNotionPageToolUI })), { ssr: false } ); const CreateLinearIssueToolUI = dynamic( - () => import("@/components/tool-ui/linear").then(m => ({ default: m.CreateLinearIssueToolUI })), + () => import("@/components/tool-ui/linear").then((m) => ({ default: m.CreateLinearIssueToolUI })), { ssr: false } ); const UpdateLinearIssueToolUI = dynamic( - () => import("@/components/tool-ui/linear").then(m => ({ default: m.UpdateLinearIssueToolUI })), + () => import("@/components/tool-ui/linear").then((m) => ({ default: m.UpdateLinearIssueToolUI })), { ssr: false } ); const DeleteLinearIssueToolUI = dynamic( - () => import("@/components/tool-ui/linear").then(m => ({ default: m.DeleteLinearIssueToolUI })), + () => import("@/components/tool-ui/linear").then((m) => ({ default: m.DeleteLinearIssueToolUI })), { ssr: false } ); const CreateGoogleDriveFileToolUI = dynamic( - () => import("@/components/tool-ui/google-drive").then(m => ({ default: m.CreateGoogleDriveFileToolUI })), + () => + import("@/components/tool-ui/google-drive").then((m) => ({ + default: m.CreateGoogleDriveFileToolUI, + })), { ssr: false } ); const DeleteGoogleDriveFileToolUI = dynamic( - () => import("@/components/tool-ui/google-drive").then(m => ({ default: m.DeleteGoogleDriveFileToolUI })), + () => + import("@/components/tool-ui/google-drive").then((m) => ({ + default: m.DeleteGoogleDriveFileToolUI, + })), { ssr: false } ); const CreateOneDriveFileToolUI = dynamic( - () => import("@/components/tool-ui/onedrive").then(m => ({ default: m.CreateOneDriveFileToolUI })), + () => + import("@/components/tool-ui/onedrive").then((m) => ({ default: m.CreateOneDriveFileToolUI })), { ssr: false } ); const DeleteOneDriveFileToolUI = dynamic( - () => import("@/components/tool-ui/onedrive").then(m => ({ default: m.DeleteOneDriveFileToolUI })), + () => + import("@/components/tool-ui/onedrive").then((m) => ({ default: m.DeleteOneDriveFileToolUI })), { ssr: false } ); const CreateDropboxFileToolUI = dynamic( - () => import("@/components/tool-ui/dropbox").then(m => ({ default: m.CreateDropboxFileToolUI })), + () => + import("@/components/tool-ui/dropbox").then((m) => ({ default: m.CreateDropboxFileToolUI })), { ssr: false } ); const DeleteDropboxFileToolUI = dynamic( - () => import("@/components/tool-ui/dropbox").then(m => ({ default: m.DeleteDropboxFileToolUI })), + () => + import("@/components/tool-ui/dropbox").then((m) => ({ default: m.DeleteDropboxFileToolUI })), { ssr: false } ); const CreateCalendarEventToolUI = dynamic( - () => import("@/components/tool-ui/google-calendar").then(m => ({ default: m.CreateCalendarEventToolUI })), + () => + import("@/components/tool-ui/google-calendar").then((m) => ({ + default: m.CreateCalendarEventToolUI, + })), { ssr: false } ); const UpdateCalendarEventToolUI = dynamic( - () => import("@/components/tool-ui/google-calendar").then(m => ({ default: m.UpdateCalendarEventToolUI })), + () => + import("@/components/tool-ui/google-calendar").then((m) => ({ + default: m.UpdateCalendarEventToolUI, + })), { ssr: false } ); const DeleteCalendarEventToolUI = dynamic( - () => import("@/components/tool-ui/google-calendar").then(m => ({ default: m.DeleteCalendarEventToolUI })), + () => + import("@/components/tool-ui/google-calendar").then((m) => ({ + default: m.DeleteCalendarEventToolUI, + })), { ssr: false } ); const CreateGmailDraftToolUI = dynamic( - () => import("@/components/tool-ui/gmail").then(m => ({ default: m.CreateGmailDraftToolUI })), + () => import("@/components/tool-ui/gmail").then((m) => ({ default: m.CreateGmailDraftToolUI })), { ssr: false } ); const UpdateGmailDraftToolUI = dynamic( - () => import("@/components/tool-ui/gmail").then(m => ({ default: m.UpdateGmailDraftToolUI })), + () => import("@/components/tool-ui/gmail").then((m) => ({ default: m.UpdateGmailDraftToolUI })), { ssr: false } ); const SendGmailEmailToolUI = dynamic( - () => import("@/components/tool-ui/gmail").then(m => ({ default: m.SendGmailEmailToolUI })), + () => import("@/components/tool-ui/gmail").then((m) => ({ default: m.SendGmailEmailToolUI })), { ssr: false } ); const TrashGmailEmailToolUI = dynamic( - () => import("@/components/tool-ui/gmail").then(m => ({ default: m.TrashGmailEmailToolUI })), + () => import("@/components/tool-ui/gmail").then((m) => ({ default: m.TrashGmailEmailToolUI })), { ssr: false } ); const CreateJiraIssueToolUI = dynamic( - () => import("@/components/tool-ui/jira").then(m => ({ default: m.CreateJiraIssueToolUI })), + () => import("@/components/tool-ui/jira").then((m) => ({ default: m.CreateJiraIssueToolUI })), { ssr: false } ); const UpdateJiraIssueToolUI = dynamic( - () => import("@/components/tool-ui/jira").then(m => ({ default: m.UpdateJiraIssueToolUI })), + () => import("@/components/tool-ui/jira").then((m) => ({ default: m.UpdateJiraIssueToolUI })), { ssr: false } ); const DeleteJiraIssueToolUI = dynamic( - () => import("@/components/tool-ui/jira").then(m => ({ default: m.DeleteJiraIssueToolUI })), + () => import("@/components/tool-ui/jira").then((m) => ({ default: m.DeleteJiraIssueToolUI })), { ssr: false } ); const CreateConfluencePageToolUI = dynamic( - () => import("@/components/tool-ui/confluence").then(m => ({ default: m.CreateConfluencePageToolUI })), + () => + import("@/components/tool-ui/confluence").then((m) => ({ + default: m.CreateConfluencePageToolUI, + })), { ssr: false } ); const UpdateConfluencePageToolUI = dynamic( - () => import("@/components/tool-ui/confluence").then(m => ({ default: m.UpdateConfluencePageToolUI })), + () => + import("@/components/tool-ui/confluence").then((m) => ({ + default: m.UpdateConfluencePageToolUI, + })), { ssr: false } ); const DeleteConfluencePageToolUI = dynamic( - () => import("@/components/tool-ui/confluence").then(m => ({ default: m.DeleteConfluencePageToolUI })), + () => + import("@/components/tool-ui/confluence").then((m) => ({ + default: m.DeleteConfluencePageToolUI, + })), { ssr: false } ); diff --git a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx index 20762c1b3..24ead1552 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connect-forms/index.tsx @@ -25,16 +25,38 @@ export interface ConnectFormProps { export type ConnectFormComponent = FC; const formMap: Record Promise<{ default: FC }>> = { - TAVILY_API: () => import("./components/tavily-api-connect-form").then(m => ({ default: m.TavilyApiConnectForm })), - LINKUP_API: () => import("./components/linkup-api-connect-form").then(m => ({ default: m.LinkupApiConnectForm })), - BAIDU_SEARCH_API: () => import("./components/baidu-search-api-connect-form").then(m => ({ default: m.BaiduSearchApiConnectForm })), - ELASTICSEARCH_CONNECTOR: () => import("./components/elasticsearch-connect-form").then(m => ({ default: m.ElasticsearchConnectForm })), - BOOKSTACK_CONNECTOR: () => import("./components/bookstack-connect-form").then(m => ({ default: m.BookStackConnectForm })), - GITHUB_CONNECTOR: () => import("./components/github-connect-form").then(m => ({ default: m.GithubConnectForm })), - LUMA_CONNECTOR: () => import("./components/luma-connect-form").then(m => ({ default: m.LumaConnectForm })), - CIRCLEBACK_CONNECTOR: () => import("./components/circleback-connect-form").then(m => ({ default: m.CirclebackConnectForm })), - MCP_CONNECTOR: () => import("./components/mcp-connect-form").then(m => ({ default: m.MCPConnectForm })), - OBSIDIAN_CONNECTOR: () => import("./components/obsidian-connect-form").then(m => ({ default: m.ObsidianConnectForm })), + TAVILY_API: () => + import("./components/tavily-api-connect-form").then((m) => ({ + default: m.TavilyApiConnectForm, + })), + LINKUP_API: () => + import("./components/linkup-api-connect-form").then((m) => ({ + default: m.LinkupApiConnectForm, + })), + BAIDU_SEARCH_API: () => + import("./components/baidu-search-api-connect-form").then((m) => ({ + default: m.BaiduSearchApiConnectForm, + })), + ELASTICSEARCH_CONNECTOR: () => + import("./components/elasticsearch-connect-form").then((m) => ({ + default: m.ElasticsearchConnectForm, + })), + BOOKSTACK_CONNECTOR: () => + import("./components/bookstack-connect-form").then((m) => ({ + default: m.BookStackConnectForm, + })), + GITHUB_CONNECTOR: () => + import("./components/github-connect-form").then((m) => ({ default: m.GithubConnectForm })), + LUMA_CONNECTOR: () => + import("./components/luma-connect-form").then((m) => ({ default: m.LumaConnectForm })), + CIRCLEBACK_CONNECTOR: () => + import("./components/circleback-connect-form").then((m) => ({ + default: m.CirclebackConnectForm, + })), + MCP_CONNECTOR: () => + import("./components/mcp-connect-form").then((m) => ({ default: m.MCPConnectForm })), + OBSIDIAN_CONNECTOR: () => + import("./components/obsidian-connect-form").then((m) => ({ default: m.ObsidianConnectForm })), }; const componentCache = new Map(); diff --git a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx index fc378612f..e3d6641fc 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx +++ b/surfsense_web/components/assistant-ui/connector-popup/connector-configs/index.tsx @@ -14,29 +14,53 @@ export interface ConnectorConfigProps { export type ConnectorConfigComponent = FC; const configMap: Record Promise<{ default: FC }>> = { - GOOGLE_DRIVE_CONNECTOR: () => import("./components/google-drive-config").then(m => ({ default: m.GoogleDriveConfig })), - TAVILY_API: () => import("./components/tavily-api-config").then(m => ({ default: m.TavilyApiConfig })), - LINKUP_API: () => import("./components/linkup-api-config").then(m => ({ default: m.LinkupApiConfig })), - BAIDU_SEARCH_API: () => import("./components/baidu-search-api-config").then(m => ({ default: m.BaiduSearchApiConfig })), - WEBCRAWLER_CONNECTOR: () => import("./components/webcrawler-config").then(m => ({ default: m.WebcrawlerConfig })), - ELASTICSEARCH_CONNECTOR: () => import("./components/elasticsearch-config").then(m => ({ default: m.ElasticsearchConfig })), - SLACK_CONNECTOR: () => import("./components/slack-config").then(m => ({ default: m.SlackConfig })), - DISCORD_CONNECTOR: () => import("./components/discord-config").then(m => ({ default: m.DiscordConfig })), - TEAMS_CONNECTOR: () => import("./components/teams-config").then(m => ({ default: m.TeamsConfig })), - DROPBOX_CONNECTOR: () => import("./components/dropbox-config").then(m => ({ default: m.DropboxConfig })), - ONEDRIVE_CONNECTOR: () => import("./components/onedrive-config").then(m => ({ default: m.OneDriveConfig })), - CONFLUENCE_CONNECTOR: () => import("./components/confluence-config").then(m => ({ default: m.ConfluenceConfig })), - BOOKSTACK_CONNECTOR: () => import("./components/bookstack-config").then(m => ({ default: m.BookStackConfig })), - GITHUB_CONNECTOR: () => import("./components/github-config").then(m => ({ default: m.GithubConfig })), - JIRA_CONNECTOR: () => import("./components/jira-config").then(m => ({ default: m.JiraConfig })), - CLICKUP_CONNECTOR: () => import("./components/clickup-config").then(m => ({ default: m.ClickUpConfig })), - LUMA_CONNECTOR: () => import("./components/luma-config").then(m => ({ default: m.LumaConfig })), - CIRCLEBACK_CONNECTOR: () => import("./components/circleback-config").then(m => ({ default: m.CirclebackConfig })), - MCP_CONNECTOR: () => import("./components/mcp-config").then(m => ({ default: m.MCPConfig })), - OBSIDIAN_CONNECTOR: () => import("./components/obsidian-config").then(m => ({ default: m.ObsidianConfig })), - COMPOSIO_GOOGLE_DRIVE_CONNECTOR: () => import("./components/composio-drive-config").then(m => ({ default: m.ComposioDriveConfig })), - COMPOSIO_GMAIL_CONNECTOR: () => import("./components/composio-gmail-config").then(m => ({ default: m.ComposioGmailConfig })), - COMPOSIO_GOOGLE_CALENDAR_CONNECTOR: () => import("./components/composio-calendar-config").then(m => ({ default: m.ComposioCalendarConfig })), + GOOGLE_DRIVE_CONNECTOR: () => + import("./components/google-drive-config").then((m) => ({ default: m.GoogleDriveConfig })), + TAVILY_API: () => + import("./components/tavily-api-config").then((m) => ({ default: m.TavilyApiConfig })), + LINKUP_API: () => + import("./components/linkup-api-config").then((m) => ({ default: m.LinkupApiConfig })), + BAIDU_SEARCH_API: () => + import("./components/baidu-search-api-config").then((m) => ({ + default: m.BaiduSearchApiConfig, + })), + WEBCRAWLER_CONNECTOR: () => + import("./components/webcrawler-config").then((m) => ({ default: m.WebcrawlerConfig })), + ELASTICSEARCH_CONNECTOR: () => + import("./components/elasticsearch-config").then((m) => ({ default: m.ElasticsearchConfig })), + SLACK_CONNECTOR: () => + import("./components/slack-config").then((m) => ({ default: m.SlackConfig })), + DISCORD_CONNECTOR: () => + import("./components/discord-config").then((m) => ({ default: m.DiscordConfig })), + TEAMS_CONNECTOR: () => + import("./components/teams-config").then((m) => ({ default: m.TeamsConfig })), + DROPBOX_CONNECTOR: () => + import("./components/dropbox-config").then((m) => ({ default: m.DropboxConfig })), + ONEDRIVE_CONNECTOR: () => + import("./components/onedrive-config").then((m) => ({ default: m.OneDriveConfig })), + CONFLUENCE_CONNECTOR: () => + import("./components/confluence-config").then((m) => ({ default: m.ConfluenceConfig })), + BOOKSTACK_CONNECTOR: () => + import("./components/bookstack-config").then((m) => ({ default: m.BookStackConfig })), + GITHUB_CONNECTOR: () => + import("./components/github-config").then((m) => ({ default: m.GithubConfig })), + JIRA_CONNECTOR: () => import("./components/jira-config").then((m) => ({ default: m.JiraConfig })), + CLICKUP_CONNECTOR: () => + import("./components/clickup-config").then((m) => ({ default: m.ClickUpConfig })), + LUMA_CONNECTOR: () => import("./components/luma-config").then((m) => ({ default: m.LumaConfig })), + CIRCLEBACK_CONNECTOR: () => + import("./components/circleback-config").then((m) => ({ default: m.CirclebackConfig })), + MCP_CONNECTOR: () => import("./components/mcp-config").then((m) => ({ default: m.MCPConfig })), + OBSIDIAN_CONNECTOR: () => + import("./components/obsidian-config").then((m) => ({ default: m.ObsidianConfig })), + COMPOSIO_GOOGLE_DRIVE_CONNECTOR: () => + import("./components/composio-drive-config").then((m) => ({ default: m.ComposioDriveConfig })), + COMPOSIO_GMAIL_CONNECTOR: () => + import("./components/composio-gmail-config").then((m) => ({ default: m.ComposioGmailConfig })), + COMPOSIO_GOOGLE_CALENDAR_CONNECTOR: () => + import("./components/composio-calendar-config").then((m) => ({ + default: m.ComposioCalendarConfig, + })), }; const componentCache = new Map(); diff --git a/surfsense_web/components/homepage/hero-section.tsx b/surfsense_web/components/homepage/hero-section.tsx index 1e31c41da..ba5a9d9ac 100644 --- a/surfsense_web/components/homepage/hero-section.tsx +++ b/surfsense_web/components/homepage/hero-section.tsx @@ -59,13 +59,15 @@ const TAB_ITEMS = [ }, { title: "Extreme Assist", - description: "Get inline writing suggestions powered by your knowledge base as you type in any app.", + description: + "Get inline writing suggestions powered by your knowledge base as you type in any app.", src: "/homepage/hero_tutorial/extreme_assist.mp4", featured: true, }, { title: "Watch Local Folder", - description: "Watch a local folder and automatically sync file changes to your knowledge base. Works great with Obsidian vaults.", + description: + "Watch a local folder and automatically sync file changes to your knowledge base. Works great with Obsidian vaults.", src: "/homepage/hero_tutorial/folder_watch.mp4", featured: true, }, @@ -84,7 +86,8 @@ const TAB_ITEMS = [ // }, { title: "Video & Presentations", - description: "Create short videos and editable presentations with AI-generated visuals and narration from your sources.", + description: + "Create short videos and editable presentations with AI-generated visuals and narration from your sources.", src: "/homepage/hero_tutorial/video_gen_surf.mp4", featured: false, }, @@ -343,7 +346,12 @@ function DownloadButton() { ))} - + All downloads @@ -498,4 +506,3 @@ const TabVideo = memo(function TabVideo({ src }: { src: string }) { }); const GITHUB_RELEASES_URL = "https://github.com/MODSetter/SurfSense/releases/latest"; - diff --git a/surfsense_web/components/homepage/navbar.tsx b/surfsense_web/components/homepage/navbar.tsx index 7faf45dab..4961199aa 100644 --- a/surfsense_web/components/homepage/navbar.tsx +++ b/surfsense_web/components/homepage/navbar.tsx @@ -144,15 +144,8 @@ const MobileNav = ({ navItems, isScrolled, scrolledBgClassName }: any) => { ref={navRef} animate={{ borderRadius: open ? "4px" : "2rem" }} key={String(open)} - className={cn( - "relative mx-auto flex w-full max-w-[calc(100vw-2rem)] flex-col items-center justify-between px-4 py-2 lg:hidden transition-[background-color,border-color,box-shadow] duration-300", - isScrolled - ? (scrolledBgClassName ?? - "bg-white/80 backdrop-blur-md border border-white/20 shadow-lg dark:bg-neutral-950/80 dark:border-neutral-800/50") - : "bg-transparent border border-transparent" - )} className={cn( - "relative mx-auto flex w-full max-w-[calc(100vw-2rem)] flex-col items-center justify-between px-4 py-2 lg:hidden transition-all duration-300", + "relative mx-auto flex w-full max-w-[calc(100vw-2rem)] flex-col items-center justify-between px-4 py-2 lg:hidden transition-[background-color,border-color,box-shadow] duration-300", isScrolled ? (scrolledBgClassName ?? "bg-white/80 backdrop-blur-md border border-white/20 shadow-lg dark:bg-neutral-950/80 dark:border-neutral-800/50") diff --git a/surfsense_web/components/homepage/why-surfsense.tsx b/surfsense_web/components/homepage/why-surfsense.tsx index 5133a4269..c50c3604a 100644 --- a/surfsense_web/components/homepage/why-surfsense.tsx +++ b/surfsense_web/components/homepage/why-surfsense.tsx @@ -1,9 +1,9 @@ "use client"; -import { useRef, useState } from "react"; -import { motion, useInView } from "motion/react"; import { IconPointerFilled } from "@tabler/icons-react"; import { Check, X } from "lucide-react"; +import { motion, useInView } from "motion/react"; +import { useRef, useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Separator } from "@/components/ui/separator"; import { cn } from "@/lib/utils"; @@ -40,8 +40,8 @@ export function WhySurfSense() { Everything NotebookLM should have been

- Open source. No data limits. No vendor lock-in. Built for teams that - care about privacy and flexibility. + Open source. No data limits. No vendor lock-in. Built for teams that care about privacy + and flexibility.

@@ -68,10 +68,7 @@ function UnlimitedSkeleton({ className }: { className?: string }) { ]; return ( -
+
{items.map((item, index) => ( {item.icon} - - {item.label} - + {item.label}
{item.notebookLm} @@ -125,10 +120,7 @@ function LLMFlexibilitySkeleton({ className }: { className?: string }) { return (
-

- {model.name} -

-

- {model.provider} -

+

{model.name}

+

{model.provider}

{selected === index && ( -
+
))} @@ -295,9 +275,7 @@ function MultiplayerSkeleton({ className }: { className?: string }) {
{collaborator.name[0]}
- - {collaborator.name} - + {collaborator.name} {collaborator.role} @@ -321,9 +299,7 @@ function FeatureCard({
{skeleton}
-

- {title} -

+

{title}

{description}

@@ -408,9 +384,7 @@ function ComparisonStrip() { transition={{ duration: 0.3, delay: 0.15 + index * 0.06 }} >
- - {row.feature} - + {row.feature} {typeof row.notebookLm === "boolean" ? ( row.notebookLm ? ( @@ -419,9 +393,7 @@ function ComparisonStrip() { ) ) : ( - - {row.notebookLm} - + {row.notebookLm} )} @@ -436,9 +408,7 @@ function ComparisonStrip() { )}
- {index !== comparisonRows.length - 1 && ( - - )} + {index !== comparisonRows.length - 1 && } ))} diff --git a/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx b/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx index 47c6c2b4a..ed54e65aa 100644 --- a/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx +++ b/surfsense_web/components/layout/ui/sidebar/SidebarSlideOutPanel.tsx @@ -91,13 +91,12 @@ export function SidebarSlideOutPanel({ {/* Panel extending from sidebar's right edge, flush with the wrapper border */}
import("@/components/tool-ui/video-presentation").then((m) => ({ default: m.GenerateVideoPresentationToolUI })), + () => + import("@/components/tool-ui/video-presentation").then((m) => ({ + default: m.GenerateVideoPresentationToolUI, + })), { ssr: false } ); diff --git a/surfsense_web/components/settings/image-model-manager.tsx b/surfsense_web/components/settings/image-model-manager.tsx index 977e42efc..55dfe3d31 100644 --- a/surfsense_web/components/settings/image-model-manager.tsx +++ b/surfsense_web/components/settings/image-model-manager.tsx @@ -79,9 +79,11 @@ export function ImageModelManager({ searchSpaceId }: ImageModelManagerProps) { const { data: access } = useAtomValue(myAccessAtom); const canCreate = - !!access && (access.is_owner || (access.permissions?.includes("image_generations:create") ?? false)); + !!access && + (access.is_owner || (access.permissions?.includes("image_generations:create") ?? false)); const canDelete = - !!access && (access.is_owner || (access.permissions?.includes("image_generations:delete") ?? false)); + !!access && + (access.is_owner || (access.permissions?.includes("image_generations:delete") ?? false)); const canUpdate = canCreate; const isReadOnly = !canCreate && !canDelete; diff --git a/surfsense_web/components/settings/model-config-manager.tsx b/surfsense_web/components/settings/model-config-manager.tsx index 82464a0ed..0b89379be 100644 --- a/surfsense_web/components/settings/model-config-manager.tsx +++ b/surfsense_web/components/settings/model-config-manager.tsx @@ -89,9 +89,12 @@ export function ModelConfigManager({ searchSpaceId }: ModelConfigManagerProps) { // Permissions const { data: access } = useAtomValue(myAccessAtom); - const canCreate = !!access && (access.is_owner || (access.permissions?.includes("llm_configs:create") ?? false)); - const canUpdate = !!access && (access.is_owner || (access.permissions?.includes("llm_configs:update") ?? false)); - const canDelete = !!access && (access.is_owner || (access.permissions?.includes("llm_configs:delete") ?? false)); + const canCreate = + !!access && (access.is_owner || (access.permissions?.includes("llm_configs:create") ?? false)); + const canUpdate = + !!access && (access.is_owner || (access.permissions?.includes("llm_configs:update") ?? false)); + const canDelete = + !!access && (access.is_owner || (access.permissions?.includes("llm_configs:delete") ?? false)); const isReadOnly = !canCreate && !canUpdate && !canDelete; // Local state diff --git a/surfsense_web/components/settings/search-space-settings-dialog.tsx b/surfsense_web/components/settings/search-space-settings-dialog.tsx index dc0627305..34d28eb2a 100644 --- a/surfsense_web/components/settings/search-space-settings-dialog.tsx +++ b/surfsense_web/components/settings/search-space-settings-dialog.tsx @@ -1,43 +1,62 @@ "use client"; -import dynamic from "next/dynamic"; import { useAtom } from "jotai"; import { Bot, Brain, Eye, FileText, Globe, ImageIcon, MessageSquare, Shield } from "lucide-react"; +import dynamic from "next/dynamic"; import { useTranslations } from "next-intl"; import type React from "react"; import { searchSpaceSettingsDialogAtom } from "@/atoms/settings/settings-dialog.atoms"; import { SettingsDialog } from "@/components/settings/settings-dialog"; const GeneralSettingsManager = dynamic( - () => import("@/components/settings/general-settings-manager").then(m => ({ default: m.GeneralSettingsManager })), + () => + import("@/components/settings/general-settings-manager").then((m) => ({ + default: m.GeneralSettingsManager, + })), { ssr: false } ); const ModelConfigManager = dynamic( - () => import("@/components/settings/model-config-manager").then(m => ({ default: m.ModelConfigManager })), + () => + import("@/components/settings/model-config-manager").then((m) => ({ + default: m.ModelConfigManager, + })), { ssr: false } ); const LLMRoleManager = dynamic( - () => import("@/components/settings/llm-role-manager").then(m => ({ default: m.LLMRoleManager })), + () => + import("@/components/settings/llm-role-manager").then((m) => ({ default: m.LLMRoleManager })), { ssr: false } ); const ImageModelManager = dynamic( - () => import("@/components/settings/image-model-manager").then(m => ({ default: m.ImageModelManager })), + () => + import("@/components/settings/image-model-manager").then((m) => ({ + default: m.ImageModelManager, + })), { ssr: false } ); const VisionModelManager = dynamic( - () => import("@/components/settings/vision-model-manager").then(m => ({ default: m.VisionModelManager })), + () => + import("@/components/settings/vision-model-manager").then((m) => ({ + default: m.VisionModelManager, + })), { ssr: false } ); const RolesManager = dynamic( - () => import("@/components/settings/roles-manager").then(m => ({ default: m.RolesManager })), + () => import("@/components/settings/roles-manager").then((m) => ({ default: m.RolesManager })), { ssr: false } ); const PromptConfigManager = dynamic( - () => import("@/components/settings/prompt-config-manager").then(m => ({ default: m.PromptConfigManager })), + () => + import("@/components/settings/prompt-config-manager").then((m) => ({ + default: m.PromptConfigManager, + })), { ssr: false } ); const PublicChatSnapshotsManager = dynamic( - () => import("@/components/public-chat-snapshots/public-chat-snapshots-manager").then(m => ({ default: m.PublicChatSnapshotsManager })), + () => + import("@/components/public-chat-snapshots/public-chat-snapshots-manager").then((m) => ({ + default: m.PublicChatSnapshotsManager, + })), { ssr: false } ); diff --git a/surfsense_web/components/settings/user-settings-dialog.tsx b/surfsense_web/components/settings/user-settings-dialog.tsx index ee0f7e62d..e755da197 100644 --- a/surfsense_web/components/settings/user-settings-dialog.tsx +++ b/surfsense_web/components/settings/user-settings-dialog.tsx @@ -1,8 +1,8 @@ "use client"; -import dynamic from "next/dynamic"; import { useAtom } from "jotai"; import { Globe, KeyRound, Monitor, Receipt, Sparkles, User } from "lucide-react"; +import dynamic from "next/dynamic"; import { useTranslations } from "next-intl"; import { useMemo } from "react"; import { userSettingsDialogAtom } from "@/atoms/settings/settings-dialog.atoms"; @@ -10,27 +10,45 @@ import { SettingsDialog } from "@/components/settings/settings-dialog"; import { usePlatform } from "@/hooks/use-platform"; const ProfileContent = dynamic( - () => import("@/app/dashboard/[search_space_id]/user-settings/components/ProfileContent").then(m => ({ default: m.ProfileContent })), + () => + import("@/app/dashboard/[search_space_id]/user-settings/components/ProfileContent").then( + (m) => ({ default: m.ProfileContent }) + ), { ssr: false } ); const ApiKeyContent = dynamic( - () => import("@/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent").then(m => ({ default: m.ApiKeyContent })), + () => + import("@/app/dashboard/[search_space_id]/user-settings/components/ApiKeyContent").then( + (m) => ({ default: m.ApiKeyContent }) + ), { ssr: false } ); const PromptsContent = dynamic( - () => import("@/app/dashboard/[search_space_id]/user-settings/components/PromptsContent").then(m => ({ default: m.PromptsContent })), + () => + import("@/app/dashboard/[search_space_id]/user-settings/components/PromptsContent").then( + (m) => ({ default: m.PromptsContent }) + ), { ssr: false } ); const CommunityPromptsContent = dynamic( - () => import("@/app/dashboard/[search_space_id]/user-settings/components/CommunityPromptsContent").then(m => ({ default: m.CommunityPromptsContent })), + () => + import( + "@/app/dashboard/[search_space_id]/user-settings/components/CommunityPromptsContent" + ).then((m) => ({ default: m.CommunityPromptsContent })), { ssr: false } ); const PurchaseHistoryContent = dynamic( - () => import("@/app/dashboard/[search_space_id]/user-settings/components/PurchaseHistoryContent").then(m => ({ default: m.PurchaseHistoryContent })), + () => + import( + "@/app/dashboard/[search_space_id]/user-settings/components/PurchaseHistoryContent" + ).then((m) => ({ default: m.PurchaseHistoryContent })), { ssr: false } ); const DesktopContent = dynamic( - () => import("@/app/dashboard/[search_space_id]/user-settings/components/DesktopContent").then(m => ({ default: m.DesktopContent })), + () => + import("@/app/dashboard/[search_space_id]/user-settings/components/DesktopContent").then( + (m) => ({ default: m.DesktopContent }) + ), { ssr: false } ); diff --git a/surfsense_web/components/sources/DocumentUploadTab.tsx b/surfsense_web/components/sources/DocumentUploadTab.tsx index 636b2bb35..124354a49 100644 --- a/surfsense_web/components/sources/DocumentUploadTab.tsx +++ b/surfsense_web/components/sources/DocumentUploadTab.tsx @@ -341,36 +341,28 @@ export function DocumentUploadTab({ ) ) : ( -
{ - if (!isElectron) fileInputRef.current?.click(); - }} - onKeyDown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); +
+ +
+

+ {isElectron ? "Select files or folder" : "Tap to select files or folder"} +

+

{t("file_size_limit")}

+
+
e.stopPropagation()} + onKeyDown={(e) => e.stopPropagation()} + > + {renderBrowseButton({ fullWidth: true })} +
+ )}