diff --git a/surfsense_backend/app/agents/new_chat/middleware/safe_summarization.py b/surfsense_backend/app/agents/new_chat/middleware/safe_summarization.py index 8248f5c8c..4ddcf334f 100644 --- a/surfsense_backend/app/agents/new_chat/middleware/safe_summarization.py +++ b/surfsense_backend/app/agents/new_chat/middleware/safe_summarization.py @@ -46,7 +46,7 @@ if TYPE_CHECKING: logger = logging.getLogger(__name__) -def _sanitize_message_content(msg: "AnyMessage") -> "AnyMessage": +def _sanitize_message_content(msg: AnyMessage) -> AnyMessage: """Return ``msg`` with ``content`` coerced to a non-``None`` value. ``get_buffer_string`` reads ``m.text`` which iterates ``self.content``; @@ -90,16 +90,14 @@ class SafeSummarizationMiddleware(SummarizationMiddleware): implementations from upstream. """ - def _filter_summary_messages( - self, messages: "list[AnyMessage]" - ) -> "list[AnyMessage]": + def _filter_summary_messages(self, messages: list[AnyMessage]) -> list[AnyMessage]: filtered = super()._filter_summary_messages(messages) return [_sanitize_message_content(m) for m in filtered] def create_safe_summarization_middleware( - model: "BaseChatModel", - backend: "BACKEND_TYPES", + model: BaseChatModel, + backend: BACKEND_TYPES, ) -> SafeSummarizationMiddleware: """Drop-in replacement for ``create_summarization_middleware``. diff --git a/surfsense_backend/tests/unit/test_error_contract.py b/surfsense_backend/tests/unit/test_error_contract.py index 81ec08b2d..ec8021290 100644 --- a/surfsense_backend/tests/unit/test_error_contract.py +++ b/surfsense_backend/tests/unit/test_error_contract.py @@ -202,9 +202,7 @@ class TestHTTPExceptionHandler: # Intentional 503s (e.g. feature flag off) must surface the developer # message so the frontend can render actionable copy. body = _assert_envelope(client.get("/http-503"), 503) - assert ( - body["error"]["message"] == "Page purchases are temporarily unavailable." - ) + assert body["error"]["message"] == "Page purchases are temporarily unavailable." assert body["error"]["message"] != GENERIC_5XX_MESSAGE def test_502_preserves_detail(self, client): diff --git a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx index 3175268d2..63ca9f5df 100644 --- a/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx +++ b/surfsense_web/app/dashboard/[search_space_id]/user-settings/components/DesktopContent.tsx @@ -200,8 +200,8 @@ export function DesktopContent() { Launch on Startup - Automatically start SurfSense when you sign in to your computer so global - shortcuts and folder sync are always available. + Automatically start SurfSense when you sign in to your computer so global shortcuts and + folder sync are always available. @@ -232,8 +232,7 @@ export function DesktopContent() { Start minimized to tray

- Skip the main window on boot — SurfSense lives in the system tray until you need - it. + Skip the main window on boot — SurfSense lives in the system tray until you need it.

new Date(b.created_at).getTime() - new Date(a.created_at).getTime() - ); + ].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); }, [pagesQuery.data, tokensQuery.data]); if (isLoading) { diff --git a/surfsense_web/components/assistant-ui/connector-popup/constants/connector-constants.ts b/surfsense_web/components/assistant-ui/connector-popup/constants/connector-constants.ts index d430e0f6c..6f60c63d6 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/constants/connector-constants.ts +++ b/surfsense_web/components/assistant-ui/connector-popup/constants/connector-constants.ts @@ -349,12 +349,7 @@ export const AUTO_INDEX_CONNECTOR_TYPES = new Set(Object.keys(AUTO_INDEX // `lib/posthog/events.ts` or per-connector tracking code. // ============================================================================ -export type ConnectorTelemetryGroup = - | "oauth" - | "composio" - | "crawler" - | "other" - | "unknown"; +export type ConnectorTelemetryGroup = "oauth" | "composio" | "crawler" | "other" | "unknown"; export interface ConnectorTelemetryMeta { connector_type: string; @@ -363,45 +358,44 @@ export interface ConnectorTelemetryMeta { is_oauth: boolean; } -const CONNECTOR_TELEMETRY_REGISTRY: ReadonlyMap = - (() => { - const map = new Map(); +const CONNECTOR_TELEMETRY_REGISTRY: ReadonlyMap = (() => { + const map = new Map(); - for (const c of OAUTH_CONNECTORS) { - map.set(c.connectorType, { - connector_type: c.connectorType, - connector_title: c.title, - connector_group: "oauth", - is_oauth: true, - }); - } - for (const c of COMPOSIO_CONNECTORS) { - map.set(c.connectorType, { - connector_type: c.connectorType, - connector_title: c.title, - connector_group: "composio", - is_oauth: true, - }); - } - for (const c of CRAWLERS) { - map.set(c.connectorType, { - connector_type: c.connectorType, - connector_title: c.title, - connector_group: "crawler", - is_oauth: false, - }); - } - for (const c of OTHER_CONNECTORS) { - map.set(c.connectorType, { - connector_type: c.connectorType, - connector_title: c.title, - connector_group: "other", - is_oauth: false, - }); - } + for (const c of OAUTH_CONNECTORS) { + map.set(c.connectorType, { + connector_type: c.connectorType, + connector_title: c.title, + connector_group: "oauth", + is_oauth: true, + }); + } + for (const c of COMPOSIO_CONNECTORS) { + map.set(c.connectorType, { + connector_type: c.connectorType, + connector_title: c.title, + connector_group: "composio", + is_oauth: true, + }); + } + for (const c of CRAWLERS) { + map.set(c.connectorType, { + connector_type: c.connectorType, + connector_title: c.title, + connector_group: "crawler", + is_oauth: false, + }); + } + for (const c of OTHER_CONNECTORS) { + map.set(c.connectorType, { + connector_type: c.connectorType, + connector_title: c.title, + connector_group: "other", + is_oauth: false, + }); + } - return map; - })(); + return map; +})(); /** * Returns telemetry metadata for a connector_type, or a minimal "unknown" diff --git a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts index 7ac903342..404ee16f0 100644 --- a/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts +++ b/surfsense_web/components/assistant-ui/connector-popup/hooks/use-connector-dialog.ts @@ -360,11 +360,7 @@ export const useConnectorDialog = () => { // Set connecting state immediately to disable button and show spinner setConnectingId(connector.id); - trackConnectorSetupStarted( - Number(searchSpaceId), - connector.connectorType, - "oauth_click" - ); + trackConnectorSetupStarted(Number(searchSpaceId), connector.connectorType, "oauth_click"); try { // Check if authEndpoint already has query parameters @@ -487,11 +483,7 @@ export const useConnectorDialog = () => { (connectorType: string) => { if (!searchSpaceId) return; - trackConnectorSetupStarted( - Number(searchSpaceId), - connectorType, - "non_oauth_click" - ); + trackConnectorSetupStarted(Number(searchSpaceId), connectorType, "non_oauth_click"); // Handle Obsidian specifically on Desktop & Cloud if (connectorType === EnumConnectorName.OBSIDIAN_CONNECTOR && !selfHosted && isDesktop) { diff --git a/surfsense_web/components/free-chat/free-chat-page.tsx b/surfsense_web/components/free-chat/free-chat-page.tsx index b389a8489..deac1fd00 100644 --- a/surfsense_web/components/free-chat/free-chat-page.tsx +++ b/surfsense_web/components/free-chat/free-chat-page.tsx @@ -210,8 +210,7 @@ export function FreeChatPage() { trackAnonymousChatMessageSent({ modelSlug, messageLength: userQuery.trim().length, - hasUploadedDoc: - anonMode.isAnonymous && anonMode.uploadedDoc !== null ? true : false, + hasUploadedDoc: anonMode.isAnonymous && anonMode.uploadedDoc !== null ? true : false, surface: "free_chat_page", }); diff --git a/surfsense_web/components/homepage/features-bento-grid.tsx b/surfsense_web/components/homepage/features-bento-grid.tsx index 835ccd2c2..7406223de 100644 --- a/surfsense_web/components/homepage/features-bento-grid.tsx +++ b/surfsense_web/components/homepage/features-bento-grid.tsx @@ -426,15 +426,50 @@ const AiSortIllustration = () => ( AI File Sorting illustration showing automatic folder organization {/* Scattered documents on the left */} - - - + + + {/* AI sparkle / magic in the center */} - - + + @@ -442,51 +477,208 @@ const AiSortIllustration = () => ( {/* Animated sorting arrows */} - + - + - + - + {/* Organized folder tree on the right */} {/* Root folder */} - - - + + + {/* Subfolder 1 */} - - - - - + + + + + {/* Subfolder 2 */} - - - - - + + + + + {/* Subfolder 3 */} - - - - - + + + + + {/* Sparkle accents */} @@ -495,10 +687,22 @@ const AiSortIllustration = () => ( - + - + diff --git a/surfsense_web/components/sources/DocumentUploadTab.tsx b/surfsense_web/components/sources/DocumentUploadTab.tsx index 65fa117f7..42fa72847 100644 --- a/surfsense_web/components/sources/DocumentUploadTab.tsx +++ b/surfsense_web/components/sources/DocumentUploadTab.tsx @@ -546,35 +546,36 @@ export function DocumentUploadTab({ ) ) : ( -
{ - if (!isElectron) fileInputRef.current?.click(); - }} - onKeyDown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); + // biome-ignore lint/a11y/useSemanticElements: cannot use
)}