diff --git a/src/app/(app)/giellie/page.test.tsx b/src/app/(app)/giellie/page.test.tsx
new file mode 100644
index 0000000..fb91b8e
--- /dev/null
+++ b/src/app/(app)/giellie/page.test.tsx
@@ -0,0 +1,61 @@
+import { describe, it, expect, vi } from "vitest";
+import { render, screen, fireEvent } from "@testing-library/react";
+
+const newChat = vi.fn();
+const openChat = vi.fn();
+
+vi.mock("@/components/giellie/use-giellie", () => ({
+ useGielie: () => ({
+ ready: { ready: true },
+ chats: [
+ { id: "a", title: "Chat A", updated: "" },
+ { id: "b", title: "Chat B", updated: "" },
+ ],
+ chat: { id: "a", title: "Chat A", updated: "", messages: [], pending_action: null, attachments: [] },
+ liveText: "",
+ activity: [],
+ pending: null,
+ busy: false,
+ error: "",
+ openChat,
+ newChat,
+ send: vi.fn(),
+ decide: vi.fn(),
+ }),
+}));
+
+import GielliePage from "./page";
+
+const toggle = () => screen.getByRole("switch", { name: /trust this chat/i });
+
+describe("GielliePage trust toggle", () => {
+ it("shows the email caveat as visible text", () => {
+ render();
+ expect(screen.getByText(/emails always ask/i)).toBeTruthy();
+ });
+
+ it("resets trust when another chat is opened", () => {
+ render();
+ fireEvent.click(toggle());
+ expect(toggle().getAttribute("aria-checked")).toBe("true");
+ fireEvent.click(screen.getByText("Chat B"));
+ expect(openChat).toHaveBeenCalledWith("b");
+ expect(toggle().getAttribute("aria-checked")).toBe("false");
+ });
+
+ it("resets trust on New chat", () => {
+ render();
+ fireEvent.click(toggle());
+ fireEvent.click(screen.getByRole("button", { name: /new chat/i }));
+ expect(newChat).toHaveBeenCalled();
+ expect(toggle().getAttribute("aria-checked")).toBe("false");
+ });
+
+ it("closes the mobile chat list with the close button", () => {
+ render();
+ fireEvent.click(screen.getByRole("button", { name: "Chats" }));
+ expect(screen.getByRole("button", { name: /close chats/i })).toBeTruthy();
+ fireEvent.click(screen.getByRole("button", { name: /close chats/i }));
+ expect(screen.queryByRole("button", { name: /close chats/i })).toBeNull();
+ });
+});
diff --git a/src/app/(app)/giellie/page.tsx b/src/app/(app)/giellie/page.tsx
index 123bc31..bc98076 100644
--- a/src/app/(app)/giellie/page.tsx
+++ b/src/app/(app)/giellie/page.tsx
@@ -2,7 +2,7 @@
import Link from "next/link";
import { useState } from "react";
-import { Bot, Menu } from "lucide-react";
+import { Bot, Menu, X } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Switch } from "@/components/ui/switch";
import { ChatList } from "@/components/giellie/chat-list";
@@ -18,24 +18,47 @@ export default function GielliePage() {
const [showList, setShowList] = useState(false);
const notReady = g.ready && !g.ready.ready;
+ const errorLine = g.error ?
{g.error}
: null;
return (
-
+ // Height = viewport minus the (app) layout chrome around {children}:
+ // below lg: 2px brand stripe + 56px (pt-14 mobile top bar) + 20px + 20px (p-5) = 98px
+ // lg+: 2px brand stripe + 32px + 32px (p-8) = 66px
+ // dvh (not vh) so the iOS Safari toolbar doesn't push the composer off-screen.
+
+ {showList && (
+
setShowList(false)}
+ />
+ )}