diff --git a/apps/loopover-miner-ui/src/chat-rail.test.tsx b/apps/loopover-miner-ui/src/chat-rail.test.tsx
index 6290908b33..87b199a9e8 100644
--- a/apps/loopover-miner-ui/src/chat-rail.test.tsx
+++ b/apps/loopover-miner-ui/src/chat-rail.test.tsx
@@ -1,4 +1,5 @@
import { fireEvent, render, screen } from "@testing-library/react";
+import * as React from "react";
import { afterEach, describe, expect, it, vi } from "vitest";
// The TanStack Router lib is not under test here — RootShell's own rail-state persistence is. Stub Link so the
@@ -14,6 +15,15 @@ vi.mock("@tanstack/react-router", async () => {
};
});
+// Stateful stub so the #7792 close/reopen test can assert in-rail React state survives the mobile sheet cycle
+// without standing up the real chat backend / streaming stack.
+vi.mock("./components/chat/conversation", () => ({
+ ChatConversation: () => {
+ const [draft, setDraft] = React.useState("");
+ return setDraft(event.target.value)} />;
+ },
+}));
+
import { ChatRail } from "./components/chat-rail";
import { RootShell } from "./routes/__root";
@@ -81,6 +91,25 @@ describe("ChatRail (#6513)", () => {
expect(screen.getByRole("dialog")).toBeTruthy(); // Sheet content
expect(screen.queryByRole("complementary")).toBeNull(); // never the docked panel on mobile
});
+
+ it("preserves chat draft state across a mobile sheet close/reopen cycle (#7792)", () => {
+ setViewport(400);
+ const onOpenChange = vi.fn();
+ const { rerender } = render();
+
+ const draft = screen.getByRole("textbox", { name: /chat draft/i });
+ fireEvent.change(draft, { target: { value: "still typing…" } });
+ expect((draft as HTMLInputElement).value).toBe("still typing…");
+
+ // Close the sheet (same open=false transition accidental tap-outside / Escape / toggle would cause).
+ rerender();
+ // forceMount keeps the dialog content in the tree even while closed.
+ expect(screen.getByRole("dialog", { hidden: true })).toBeTruthy();
+
+ // Reopen — draft must still be there (RailBody never unmounted).
+ rerender();
+ expect((screen.getByRole("textbox", { name: /chat draft/i }) as HTMLInputElement).value).toBe("still typing…");
+ });
});
describe("RootShell chat-rail integration (#6513)", () => {
diff --git a/apps/loopover-miner-ui/src/components/chat-rail.tsx b/apps/loopover-miner-ui/src/components/chat-rail.tsx
index 53e3b9494e..b92478c617 100644
--- a/apps/loopover-miner-ui/src/components/chat-rail.tsx
+++ b/apps/loopover-miner-ui/src/components/chat-rail.tsx
@@ -47,7 +47,10 @@ export function ChatRail({ open, onOpenChange }: ChatRailProps) {
Chat
-
+ {/* forceMount: keep RailBody mounted when the sheet closes so chat state survives, matching the
+ desktop `