From 5c3aa72c1a825f28e6ec1cae5b2287eb6ef54ddc Mon Sep 17 00:00:00 2001 From: CREDO23 Date: Mon, 13 Jul 2026 22:17:38 +0200 Subject: [PATCH] feat(chat): add ActiveChatStreamRunner to persist streams across nav --- .../chat/active-chat-stream-runner.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 surfsense_web/components/chat/active-chat-stream-runner.tsx diff --git a/surfsense_web/components/chat/active-chat-stream-runner.tsx b/surfsense_web/components/chat/active-chat-stream-runner.tsx new file mode 100644 index 000000000..da86c0aa8 --- /dev/null +++ b/surfsense_web/components/chat/active-chat-stream-runner.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { useEffect } from "react"; +import { chatStreamStore } from "@/lib/chat/stream-engine/store"; + +/** + * Persistent, render-null host for app-wide chat-stream lifecycle. + * + * Mounted in the workspace shell (``LayoutDataProvider``), it persists across + * in-app navigation between workspace routes (``/new-chat`` -> ``/chats`` -> + * ``/automations`` and doc-tab switches) and only unmounts on real teardown + * (workspace change / app teardown). On that teardown it aborts the single + * in-flight turn — this replaces the old chat-page unmount abort, which was + * what killed streams on ordinary navigation. + * + * NOTE: the ``hitl-decision`` bridge and ``useChatSessionStateSync`` stay in + * the chat page: both need the currently-viewed thread id (HITL resume can + * only be triggered from the page's approval UI), which the shell-level runner + * does not cleanly have. + */ +export function ActiveChatStreamRunner() { + useEffect(() => { + return () => { + chatStreamStore.abortActive(); + }; + }, []); + + return null; +}