From b010468a96232032ea393c1f5f62d3665067e443 Mon Sep 17 00:00:00 2001 From: Anish Sarkar <104695310+AnishSarkar22@users.noreply.github.com> Date: Sun, 12 Jul 2026 20:30:14 +0530 Subject: [PATCH] refactor(thread): gate composer on setup status and drop select-a-model hint --- .../components/assistant-ui/thread.tsx | 44 +++++-------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git a/surfsense_web/components/assistant-ui/thread.tsx b/surfsense_web/components/assistant-ui/thread.tsx index 0cf6ef296..0cee0d078 100644 --- a/surfsense_web/components/assistant-ui/thread.tsx +++ b/surfsense_web/components/assistant-ui/thread.tsx @@ -47,11 +47,7 @@ import { import { connectorDialogOpenAtom } from "@/atoms/connector-dialog/connector-dialog.atoms"; import { connectorsAtom } from "@/atoms/connectors/connector-query.atoms"; import { membersAtom } from "@/atoms/members/members-query.atoms"; -import { - globalModelConnectionsAtom, - modelConnectionsAtom, - modelRolesAtom, -} from "@/atoms/model-connections/model-connections-query.atoms"; +import { llmSetupStatusAtomFamily } from "@/atoms/model-connections/model-connections-query.atoms"; import { currentUserAtom } from "@/atoms/user/user-query.atoms"; import { AssistantMessage } from "@/components/assistant-ui/assistant-message"; import { ChatSessionStatus } from "@/components/assistant-ui/chat-session-status"; @@ -1074,9 +1070,7 @@ const ComposerAction: FC = ({ if (url) setPendingScreenImages((prev) => [...prev, url]); }, [electronAPI, setPendingScreenImages]); - const { data: globalModelConnections } = useAtomValue(globalModelConnectionsAtom); - const { data: modelConnections } = useAtomValue(modelConnectionsAtom); - const { data: modelRoles } = useAtomValue(modelRolesAtom); + const { data: setupStatus } = useAtomValue(llmSetupStatusAtomFamily(workspaceId)); const { data: agentTools } = useAtomValue(agentToolsAtom); const disabledTools = useAtomValue(disabledToolsAtom); @@ -1157,21 +1151,13 @@ const ComposerAction: FC = ({ hydrateDisabled(); }, [hydrateDisabled]); - const hasModelConfigured = useMemo(() => { - const chatModelId = modelRoles?.chat_model_id ?? 0; - if (chatModelId === 0) { - return [...(globalModelConnections ?? []), ...(modelConnections ?? [])].some((connection) => - connection.models.some((model) => model.enabled && Boolean(model.supports_chat)) - ); - } - return [...(globalModelConnections ?? []), ...(modelConnections ?? [])].some((connection) => - connection.models.some( - (model) => model.id === chatModelId && model.enabled && Boolean(model.supports_chat) - ) - ); - }, [modelRoles?.chat_model_id, globalModelConnections, modelConnections]); + // Defense-in-depth only: the onboarding gate makes an unconfigured + // workspace unreachable, but a stale cache (e.g. another admin removed the + // last model) can briefly leave this composer mounted. Block sends silently + // until the status refetch re-engages the gate. + const isWorkspaceChatReady = setupStatus?.status === "ready"; - const isSendDisabled = isComposerEmpty || !hasModelConfigured || isBlockedByOtherUser; + const isSendDisabled = isComposerEmpty || !isWorkspaceChatReady || isBlockedByOtherUser; return (
@@ -1613,12 +1599,6 @@ const ComposerAction: FC = ({ )}
- {!hasModelConfigured && ( -
- - Select a model -
- )}
= ({ tooltip={ isBlockedByOtherUser ? "Wait for AI to finish responding" - : !hasModelConfigured - ? "Please select a model to start chatting" - : isComposerEmpty - ? "Enter a message or add a screenshot to send" - : "Send message" + : isComposerEmpty + ? "Enter a message or add a screenshot to send" + : "Send message" } side="bottom" type="submit"