diff --git a/surfsense_backend/app/app.py b/surfsense_backend/app/app.py index b308d3770..7f1fbfabe 100644 --- a/surfsense_backend/app/app.py +++ b/surfsense_backend/app/app.py @@ -51,8 +51,7 @@ from app.gateway.inbox_worker import ( start_gateway_inbox_worker, stop_gateway_inbox_worker, ) -from app.observability import analytics as ph_analytics -from app.observability import metrics as ot_metrics +from app.observability import analytics as ph_analytics, metrics as ot_metrics from app.observability.bootstrap import init_otel, shutdown_otel from app.rate_limiter import get_real_client_ip, limiter from app.routes import router as crud_router @@ -843,11 +842,7 @@ class PatApiAnalyticsMiddleware(BaseHTTPMiddleware): response = await call_next(request) with contextlib.suppress(Exception): ctx = getattr(request.state, "auth_context", None) - if ( - ctx is not None - and ctx.method == "pat" - and ph_analytics.is_enabled() - ): + if ctx is not None and ctx.method == "pat" and ph_analytics.is_enabled(): # Use the route *template* (e.g. /documents/{id}) to keep the # ``route`` property low-cardinality; fall back to the raw path. route = request.scope.get("route") diff --git a/surfsense_backend/app/automations/runtime/executor.py b/surfsense_backend/app/automations/runtime/executor.py index cbf682d60..a515c6563 100644 --- a/surfsense_backend/app/automations/runtime/executor.py +++ b/surfsense_backend/app/automations/runtime/executor.py @@ -41,9 +41,7 @@ def _capture_run_outcome(run: AutomationRun, status: str) -> None: "status": status, "trigger_type": run.trigger.type.value if run.trigger else None, }, - groups={"workspace": str(workspace_id)} - if workspace_id is not None - else None, + groups={"workspace": str(workspace_id)} if workspace_id is not None else None, ) diff --git a/surfsense_backend/app/proprietary/platforms/walmart/parsers.py b/surfsense_backend/app/proprietary/platforms/walmart/parsers.py index 5ea32c4d1..110f05e75 100644 --- a/surfsense_backend/app/proprietary/platforms/walmart/parsers.py +++ b/surfsense_backend/app/proprietary/platforms/walmart/parsers.py @@ -132,7 +132,9 @@ def _breadcrumbs(product: dict[str, Any]) -> list[str]: path = dig(product, "category", "path") if not isinstance(path, list): return [] - return [node["name"] for node in path if isinstance(node, dict) and node.get("name")] + return [ + node["name"] for node in path if isinstance(node, dict) and node.get("name") + ] def _reviews_sample( diff --git a/surfsense_backend/app/routes/anonymous_chat_routes.py b/surfsense_backend/app/routes/anonymous_chat_routes.py index f53217826..3b48ab209 100644 --- a/surfsense_backend/app/routes/anonymous_chat_routes.py +++ b/surfsense_backend/app/routes/anonymous_chat_routes.py @@ -14,16 +14,16 @@ from fastapi.responses import StreamingResponse from pydantic import BaseModel, Field from app.config import config -from app.observability import analytics as ph_analytics -from app.tasks.chat.streaming.flows.shared.analytics import ( - build_llm_callback_handler, -) from app.etl_pipeline.file_classifier import ( DIRECT_CONVERT_EXTENSIONS, PLAINTEXT_EXTENSIONS, ) +from app.observability import analytics as ph_analytics from app.rate_limiter import limiter from app.tasks.chat.streaming.errors.classifier import classify_stream_exception +from app.tasks.chat.streaming.flows.shared.analytics import ( + build_llm_callback_handler, +) logger = logging.getLogger(__name__) diff --git a/surfsense_backend/app/routes/image_generation_routes.py b/surfsense_backend/app/routes/image_generation_routes.py index e4aab3f80..59a214445 100644 --- a/surfsense_backend/app/routes/image_generation_routes.py +++ b/surfsense_backend/app/routes/image_generation_routes.py @@ -17,7 +17,6 @@ from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.auth.context import AuthContext -from app.observability import analytics as ph_analytics from app.config import config from app.db import ( ImageGeneration, @@ -27,6 +26,7 @@ from app.db import ( WorkspaceMembership, get_async_session, ) +from app.observability import analytics as ph_analytics from app.schemas import ( ImageGenerationCreate, ImageGenerationListRead, diff --git a/surfsense_backend/app/routes/search_source_connectors_routes.py b/surfsense_backend/app/routes/search_source_connectors_routes.py index ba0899db3..03f198d52 100644 --- a/surfsense_backend/app/routes/search_source_connectors_routes.py +++ b/surfsense_backend/app/routes/search_source_connectors_routes.py @@ -45,8 +45,11 @@ from app.db import ( get_async_session, ) from app.notifications.service import NotificationService -from app.observability import analytics as ph_analytics -from app.observability import metrics as ot_metrics, otel as ot +from app.observability import ( + analytics as ph_analytics, + metrics as ot_metrics, + otel as ot, +) from app.schemas import ( GoogleDriveIndexRequest, MCPConnectorCreate, diff --git a/surfsense_backend/app/services/export_service.py b/surfsense_backend/app/services/export_service.py index e718c25d1..8e603dad5 100644 --- a/surfsense_backend/app/services/export_service.py +++ b/surfsense_backend/app/services/export_service.py @@ -247,9 +247,7 @@ async def build_export_zip( suffix = used_paths[file_path] base_name = f"{base_name}_{suffix}" file_path = ( - f"{dir_path}/{base_name}.md" - if dir_path - else f"{base_name}.md" + f"{dir_path}/{base_name}.md" if dir_path else f"{base_name}.md" ) used_paths[file_path] = used_paths.get(file_path, 0) + 1 diff --git a/surfsense_backend/app/services/okf/__init__.py b/surfsense_backend/app/services/okf/__init__.py index 1bcb1ccb1..e6a66dd39 100644 --- a/surfsense_backend/app/services/okf/__init__.py +++ b/surfsense_backend/app/services/okf/__init__.py @@ -37,6 +37,8 @@ from app.services.okf.validator import ( __all__ = [ "INDEX_FILENAME", "LOG_FILENAME", + "RECOMMENDED_FRONTMATTER_KEYS", + "REQUIRED_FRONTMATTER_KEYS", "ConceptRef", "LogEntry", "SubdirRef", @@ -44,13 +46,11 @@ __all__ = [ "document_to_concept", "folder_to_index", "folder_to_log", - "render_frontmatter", + "is_conformant_concept", "okf_resource", "okf_type", - "RECOMMENDED_FRONTMATTER_KEYS", - "REQUIRED_FRONTMATTER_KEYS", - "is_conformant_concept", "parse_frontmatter", + "render_frontmatter", "validate_bundle", "validate_concept", ] diff --git a/surfsense_backend/app/services/okf/serializer.py b/surfsense_backend/app/services/okf/serializer.py index c866badf9..f118b79cf 100644 --- a/surfsense_backend/app/services/okf/serializer.py +++ b/surfsense_backend/app/services/okf/serializer.py @@ -49,7 +49,11 @@ def build_frontmatter(document: Document) -> dict[str, Any]: Only ``type`` is required; recommended keys are included only when we have a value. Insertion order is preserved in the emitted YAML. """ - metadata = document.document_metadata if isinstance(document.document_metadata, dict) else {} + metadata = ( + document.document_metadata + if isinstance(document.document_metadata, dict) + else {} + ) frontmatter: dict[str, Any] = {"type": okf_type(document.document_type)} diff --git a/surfsense_backend/app/services/okf/type_mapping.py b/surfsense_backend/app/services/okf/type_mapping.py index 2f1155369..1be36166e 100644 --- a/surfsense_backend/app/services/okf/type_mapping.py +++ b/surfsense_backend/app/services/okf/type_mapping.py @@ -50,7 +50,9 @@ OKF_TYPE_BY_DOCUMENT_TYPE: dict[DocumentType, str] = { } -def _coerce_document_type(document_type: DocumentType | str | None) -> DocumentType | None: +def _coerce_document_type( + document_type: DocumentType | str | None, +) -> DocumentType | None: if document_type is None: return None try: diff --git a/surfsense_backend/app/tasks/celery_tasks/connector_tasks.py b/surfsense_backend/app/tasks/celery_tasks/connector_tasks.py index fb08215b5..2c1db716b 100644 --- a/surfsense_backend/app/tasks/celery_tasks/connector_tasks.py +++ b/surfsense_backend/app/tasks/celery_tasks/connector_tasks.py @@ -9,8 +9,11 @@ from collections.abc import Awaitable, Callable from celery import current_task from app.celery_app import celery_app -from app.observability import analytics as ph_analytics -from app.observability import metrics as ot_metrics, otel as ot +from app.observability import ( + analytics as ph_analytics, + metrics as ot_metrics, + otel as ot, +) from app.tasks.celery_tasks import ( get_celery_session_maker, run_async_celery_task as _run_async_celery_task, diff --git a/surfsense_backend/app/tasks/celery_tasks/document_tasks.py b/surfsense_backend/app/tasks/celery_tasks/document_tasks.py index 8e20275b7..1b457de45 100644 --- a/surfsense_backend/app/tasks/celery_tasks/document_tasks.py +++ b/surfsense_backend/app/tasks/celery_tasks/document_tasks.py @@ -10,8 +10,7 @@ from uuid import UUID from app.celery_app import celery_app from app.config import config from app.notifications.service import NotificationService -from app.observability import analytics as ph_analytics -from app.observability import metrics as ot_metrics +from app.observability import analytics as ph_analytics, metrics as ot_metrics from app.services.task_logging_service import TaskLoggingService from app.tasks.celery_tasks import get_celery_session_maker, run_async_celery_task from app.tasks.connector_indexers.local_folder_indexer import ( diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/orchestrator.py b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/orchestrator.py index a6544b810..6f3af2c26 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/orchestrator.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/new_chat/orchestrator.py @@ -68,6 +68,10 @@ from app.tasks.chat.streaming.flows.new_chat.title_gen import ( maybe_emit_title_update, spawn_title_task, ) +from app.tasks.chat.streaming.flows.shared.analytics import ( + build_llm_callback_handler, + capture_chat_turn_completed, +) from app.tasks.chat.streaming.flows.shared.assistant_finalize import ( finalize_assistant_message, ) @@ -97,10 +101,6 @@ from app.tasks.chat.streaming.flows.shared.rate_limit_recovery import ( log_rate_limit_recovered, reroute_to_next_auto_pin, ) -from app.tasks.chat.streaming.flows.shared.analytics import ( - build_llm_callback_handler, - capture_chat_turn_completed, -) from app.tasks.chat.streaming.flows.shared.span import ( close_chat_request_span, open_chat_request_span, diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/resume_chat/orchestrator.py b/surfsense_backend/app/tasks/chat/streaming/flows/resume_chat/orchestrator.py index 8dd749aee..932874d92 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/resume_chat/orchestrator.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/resume_chat/orchestrator.py @@ -47,6 +47,10 @@ from app.tasks.chat.streaming.flows.resume_chat.resume_routing import ( from app.tasks.chat.streaming.flows.resume_chat.runtime_context import ( build_resume_chat_runtime_context, ) +from app.tasks.chat.streaming.flows.shared.analytics import ( + build_llm_callback_handler, + capture_chat_turn_completed, +) from app.tasks.chat.streaming.flows.shared.assistant_finalize import ( finalize_assistant_message, ) @@ -76,10 +80,6 @@ from app.tasks.chat.streaming.flows.shared.rate_limit_recovery import ( log_rate_limit_recovered, reroute_to_next_auto_pin, ) -from app.tasks.chat.streaming.flows.shared.analytics import ( - build_llm_callback_handler, - capture_chat_turn_completed, -) from app.tasks.chat.streaming.flows.shared.span import ( close_chat_request_span, open_chat_request_span, diff --git a/surfsense_backend/app/tasks/chat/streaming/flows/shared/analytics.py b/surfsense_backend/app/tasks/chat/streaming/flows/shared/analytics.py index 2d311c26b..bade639bf 100644 --- a/surfsense_backend/app/tasks/chat/streaming/flows/shared/analytics.py +++ b/surfsense_backend/app/tasks/chat/streaming/flows/shared/analytics.py @@ -105,9 +105,7 @@ def capture_chat_turn_completed( groups = {"workspace": str(workspace_id)} if auth_context is not None: - analytics.capture_for( - auth_context, "chat_turn_completed", props, groups=groups - ) + analytics.capture_for(auth_context, "chat_turn_completed", props, groups=groups) else: analytics.capture( "chat_turn_completed", diff --git a/surfsense_backend/app/users.py b/surfsense_backend/app/users.py index 633b08443..47c1c5767 100644 --- a/surfsense_backend/app/users.py +++ b/surfsense_backend/app/users.py @@ -20,7 +20,6 @@ from sqlalchemy.ext.asyncio import AsyncSession from app.auth.context import AuthContext from app.auth.session_cookies import access_expires_at, write_session from app.config import config -from app.observability import analytics as ph_analytics from app.db import ( Prompt, User, @@ -32,6 +31,7 @@ from app.db import ( get_default_roles_config, get_user_db, ) +from app.observability import analytics as ph_analytics from app.prompts.system_defaults import SYSTEM_PROMPT_DEFAULTS from app.utils.pat import PAT_PREFIX, maybe_touch_last_used, resolve_pat from app.utils.refresh_tokens import create_refresh_token @@ -353,6 +353,7 @@ async def get_auth_context( FastAPI-Users still handles JWT mechanics; PATs are resolved here so RBAC receives the full SurfSense principal instead of a bare User. """ + def _stash(ctx: AuthContext) -> AuthContext: # Expose the resolved principal on request.state so downstream # middleware (e.g. PostHog pat_api_request attribution) can read it diff --git a/surfsense_backend/scripts/e2e_indeed_scraper.py b/surfsense_backend/scripts/e2e_indeed_scraper.py index 24143af4f..fda29933c 100644 --- a/surfsense_backend/scripts/e2e_indeed_scraper.py +++ b/surfsense_backend/scripts/e2e_indeed_scraper.py @@ -75,9 +75,7 @@ async def step1_search(sess, state: dict) -> bool: ) for it in items[:5]: print(f" - {it.get('jobKey')} | {it.get('title')} @ {it.get('company')}") - state["job_url"] = next( - (it["jobUrl"] for it in items if it.get("jobUrl")), None - ) + state["job_url"] = next((it["jobUrl"] for it in items if it.get("jobUrl")), None) return _check("search returned jobs", len(items) > 0, f"{len(items)} jobs") diff --git a/surfsense_backend/tests/integration/test_okf_export_bundle.py b/surfsense_backend/tests/integration/test_okf_export_bundle.py index bccd3ac4a..05869310e 100644 --- a/surfsense_backend/tests/integration/test_okf_export_bundle.py +++ b/surfsense_backend/tests/integration/test_okf_export_bundle.py @@ -52,12 +52,20 @@ async def test_export_bundle_is_okf_conformant( await db_session.flush() await _add_doc( - db_session, workspace=db_workspace, user=db_user, - title="Root Note", folder_id=None, uid="okf-export-root", + db_session, + workspace=db_workspace, + user=db_user, + title="Root Note", + folder_id=None, + uid="okf-export-root", ) await _add_doc( - db_session, workspace=db_workspace, user=db_user, - title="Nested Note", folder_id=folder.id, uid="okf-export-nested", + db_session, + workspace=db_workspace, + user=db_user, + title="Nested Note", + folder_id=folder.id, + uid="okf-export-nested", ) result = await build_export_zip(db_session, db_workspace.id) diff --git a/surfsense_backend/tests/integration/test_okf_path_identity.py b/surfsense_backend/tests/integration/test_okf_path_identity.py index 8c12d7844..2de3ebac1 100644 --- a/surfsense_backend/tests/integration/test_okf_path_identity.py +++ b/surfsense_backend/tests/integration/test_okf_path_identity.py @@ -56,9 +56,7 @@ async def _roundtrip( @pytest_asyncio.fixture -async def research_folder( - db_session: AsyncSession, db_workspace: Workspace -) -> Folder: +async def research_folder(db_session: AsyncSession, db_workspace: Workspace) -> Folder: folder = Folder(name="Research", position="0", workspace_id=db_workspace.id) db_session.add(folder) await db_session.flush() diff --git a/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py b/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py index ad3b17c9c..e4e92086f 100644 --- a/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py +++ b/surfsense_backend/tests/unit/agents/new_chat/test_path_resolver.py @@ -85,9 +85,7 @@ class TestConceptIdentityRoundTrip: def test_folder_nested_document_roundtrips(self): index = PathIndex(folder_paths={5: f"{DOCUMENTS_ROOT}/Research/AI"}) - path = doc_to_virtual_path( - doc_id=2, title="My Note", folder_id=5, index=index - ) + path = doc_to_virtual_path(doc_id=2, title="My Note", folder_id=5, index=index) assert path == f"{DOCUMENTS_ROOT}/Research/AI/My Note.xml" folder_parts, title = parse_documents_path(path) assert folder_parts == ["Research", "AI"] @@ -97,9 +95,7 @@ class TestConceptIdentityRoundTrip: # Second doc with the same title gets a " ()" suffix; parsing the # path must strip the disambiguator and recover the original title. index = PathIndex(occupants={f"{DOCUMENTS_ROOT}/Hello.xml": 7}) - path = doc_to_virtual_path( - doc_id=8, title="Hello", folder_id=None, index=index - ) + path = doc_to_virtual_path(doc_id=8, title="Hello", folder_id=None, index=index) assert path == f"{DOCUMENTS_ROOT}/Hello (8).xml" folder_parts, title = parse_documents_path(path) assert folder_parts == [] diff --git a/surfsense_backend/tests/unit/platforms/reddit/test_community_listing.py b/surfsense_backend/tests/unit/platforms/reddit/test_community_listing.py index 1955345e1..89fa60b64 100644 --- a/surfsense_backend/tests/unit/platforms/reddit/test_community_listing.py +++ b/surfsense_backend/tests/unit/platforms/reddit/test_community_listing.py @@ -47,7 +47,9 @@ async def test_community_only_scrapes_listing(monkeypatch): assert [i["id"] for i in items] == ["movies", "movies-p1"] -@pytest.mark.parametrize("raw", ["movies", "r/movies", "/r/movies/", " movies ", "R/movies"]) +@pytest.mark.parametrize( + "raw", ["movies", "r/movies", "/r/movies/", " movies ", "R/movies"] +) async def test_community_name_normalized(monkeypatch, raw): seen: list[str] = [] monkeypatch.setattr(scraper, "_subreddit_flow", _fake_subreddit_flow(seen)) diff --git a/surfsense_backend/tests/unit/services/okf/test_serializer.py b/surfsense_backend/tests/unit/services/okf/test_serializer.py index 157ed44c8..d52addf5b 100644 --- a/surfsense_backend/tests/unit/services/okf/test_serializer.py +++ b/surfsense_backend/tests/unit/services/okf/test_serializer.py @@ -62,7 +62,9 @@ def test_validator_rejects_non_conformant_documents() -> None: def test_folder_index_groups_by_type_and_lists_subdirs() -> None: index = folder_to_index( concepts=[ - ConceptRef(title="Orders", filename="orders.md", type="Note", description="x"), + ConceptRef( + title="Orders", filename="orders.md", type="Note", description="x" + ), ], subdirectories=[SubdirRef(name="tables", description="Table docs")], ) @@ -114,11 +116,7 @@ def test_export_index_files_include_root_version_and_ancestors() -> None: # A concept nested two levels deep, with no direct docs in the middle dir. files = dict( _build_index_files( - { - "Research/AI": [ - ConceptRef(title="Note", filename="note.md", type="Note") - ] - } + {"Research/AI": [ConceptRef(title="Note", filename="note.md", type="Note")]} ) ) diff --git a/surfsense_mcp/mcp_server/features/scrapers/platforms/amazon.py b/surfsense_mcp/mcp_server/features/scrapers/platforms/amazon.py index 09b703157..15efa311c 100644 --- a/surfsense_mcp/mcp_server/features/scrapers/platforms/amazon.py +++ b/surfsense_mcp/mcp_server/features/scrapers/platforms/amazon.py @@ -93,8 +93,10 @@ def register(mcp: FastMCP, client: SurfSenseClient, context: WorkspaceContext) - ] = False, country_code: Annotated[ str | None, - Field(description="Two-letter delivery country for localized pricing, " - "e.g. 'us'."), + Field( + description="Two-letter delivery country for localized pricing, " + "e.g. 'us'." + ), ] = None, zip_code: Annotated[ str | None, diff --git a/surfsense_mcp/mcp_server/features/scrapers/platforms/indeed.py b/surfsense_mcp/mcp_server/features/scrapers/platforms/indeed.py index 6ea0007f9..e9323cd32 100644 --- a/surfsense_mcp/mcp_server/features/scrapers/platforms/indeed.py +++ b/surfsense_mcp/mcp_server/features/scrapers/platforms/indeed.py @@ -56,7 +56,9 @@ def register(mcp: FastMCP, client: SurfSenseClient, context: WorkspaceContext) - ] = None, country: Annotated[ str, - Field(description="Country code selecting the Indeed domain, e.g. 'us', 'gb'."), + Field( + description="Country code selecting the Indeed domain, e.g. 'us', 'gb'." + ), ] = "us", location: Annotated[ str | None, diff --git a/surfsense_mcp/mcp_server/features/scrapers/platforms/walmart.py b/surfsense_mcp/mcp_server/features/scrapers/platforms/walmart.py index 0c11af653..33a7d51e9 100644 --- a/surfsense_mcp/mcp_server/features/scrapers/platforms/walmart.py +++ b/surfsense_mcp/mcp_server/features/scrapers/platforms/walmart.py @@ -42,7 +42,9 @@ def register(mcp: FastMCP, client: SurfSenseClient, context: WorkspaceContext) - ] = None, max_items: Annotated[ int, - Field(ge=1, le=100, description="Max products per search term or listing URL."), + Field( + ge=1, le=100, description="Max products per search term or listing URL." + ), ] = 10, include_details: Annotated[ bool, diff --git a/surfsense_web/app/dashboard/[workspace_id]/new-chat/[[...chat_id]]/page.tsx b/surfsense_web/app/dashboard/[workspace_id]/new-chat/[[...chat_id]]/page.tsx index dff264c43..70cf4c8e0 100644 --- a/surfsense_web/app/dashboard/[workspace_id]/new-chat/[[...chat_id]]/page.tsx +++ b/surfsense_web/app/dashboard/[workspace_id]/new-chat/[[...chat_id]]/page.tsx @@ -178,8 +178,7 @@ export default function NewChatPage() { const hasLiveStream = !!streamState && streamState.messages.length > 0; const isActiveThreadHydrated = hydratedMessagesRef.current.threadId === activeThreadId; const shouldHideStaleMessages = !!activeThreadId && !hasLiveStream && !isActiveThreadHydrated; - const isThreadMessagesLoading = - shouldHideStaleMessages && !threadMessagesQuery.error; + const isThreadMessagesLoading = shouldHideStaleMessages && !threadMessagesQuery.error; const runtimeMessages = shouldHideStaleMessages ? EMPTY_MESSAGES : displayMessages; // Live collaboration: sync session state and messages via Zero. Kept on the diff --git a/surfsense_web/atoms/citation/citation-panel.atom.ts b/surfsense_web/atoms/citation/citation-panel.atom.ts index f92fd6f90..e07131378 100644 --- a/surfsense_web/atoms/citation/citation-panel.atom.ts +++ b/surfsense_web/atoms/citation/citation-panel.atom.ts @@ -2,9 +2,7 @@ import { atom, type Getter, type Setter } from "jotai"; import { rightPanelCollapsedAtom, rightPanelTabAtom } from "@/atoms/layout/right-panel.atom"; /** The source the citation panel is showing: a KB chunk or a scraper run. */ -export type CitationTarget = - | { kind: "chunk"; chunkId: number } - | { kind: "run"; runId: string }; +export type CitationTarget = { kind: "chunk"; chunkId: number } | { kind: "run"; runId: string }; interface CitationPanelState { isOpen: boolean; diff --git a/surfsense_web/atoms/tabs/tabs.atom.ts b/surfsense_web/atoms/tabs/tabs.atom.ts index fbc546c42..b709bcd50 100644 --- a/surfsense_web/atoms/tabs/tabs.atom.ts +++ b/surfsense_web/atoms/tabs/tabs.atom.ts @@ -285,7 +285,11 @@ export const pruneMissingChatTabsAtom = atom(null, (get, set, missingChatIds: Se } const activeWasPruned = state.tabs.some( - (t) => t.id === state.activeTabId && t.type === "chat" && t.entityId !== null && missingChatIds.has(t.entityId) + (t) => + t.id === state.activeTabId && + t.type === "chat" && + t.entityId !== null && + missingChatIds.has(t.entityId) ); const newActiveId = activeWasPruned ? remaining[Math.min(firstMissingIdx, remaining.length - 1)].id diff --git a/surfsense_web/components/assistant-ui/composer-add-menu-drawer.tsx b/surfsense_web/components/assistant-ui/composer-add-menu-drawer.tsx index 86a923917..e48ae6323 100644 --- a/surfsense_web/components/assistant-ui/composer-add-menu-drawer.tsx +++ b/surfsense_web/components/assistant-ui/composer-add-menu-drawer.tsx @@ -74,7 +74,8 @@ type Screen = | { kind: "tools" } | { kind: "toolGroup"; label: string }; -const ROW = "flex w-full items-center gap-3 px-4 py-3 text-sm hover:bg-accent hover:text-accent-foreground transition-colors"; +const ROW = + "flex w-full items-center gap-3 px-4 py-3 text-sm hover:bg-accent hover:text-accent-foreground transition-colors"; export function ComposerAddMenuDrawer({ trigger, @@ -156,11 +157,7 @@ export function ComposerAddMenuDrawer({ Upload Files -