diff --git a/packages/dashboard/app/components/QuickChatFAB.css b/packages/dashboard/app/components/QuickChatFAB.css index e973c4cec..e4122e3d3 100644 --- a/packages/dashboard/app/components/QuickChatFAB.css +++ b/packages/dashboard/app/components/QuickChatFAB.css @@ -211,34 +211,70 @@ } .quick-chat-panel-header-actions { + --quick-chat-header-control-size: calc(var(--space-lg) + var(--space-sm) + var(--space-xs)); + display: inline-flex; align-items: center; - gap: var(--space-sm); -} - -.quick-chat-panel-header-actions .btn { - white-space: nowrap; -} - -.quick-chat-mode-toggle { - display: flex; gap: var(--space-xs); - padding: var(--space-sm) var(--space-md); - border-bottom: 1px solid var(--border); +} + +.quick-chat-panel-header-actions .btn-icon { + width: var(--quick-chat-header-control-size); + min-width: var(--quick-chat-header-control-size); + height: var(--quick-chat-header-control-size); + min-height: var(--quick-chat-header-control-size); + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid var(--border); + border-radius: var(--radius-md); + background: var(--card); + color: var(--text); +} + +.quick-chat-header-mode-toggle { + display: inline-flex; + align-items: center; + gap: calc(var(--space-xs) / 2); } .quick-chat-mode-btn { - flex: 1; - padding: var(--space-xs) var(--space-sm); + width: var(--quick-chat-header-control-size); + min-width: var(--quick-chat-header-control-size); + height: var(--quick-chat-header-control-size); + min-height: var(--quick-chat-header-control-size); + padding: 0; + display: inline-flex; + align-items: center; + justify-content: center; border: 1px solid var(--border); - border-radius: var(--radius-sm); + border-radius: var(--radius-pill); background: transparent; color: var(--text-muted); font-size: 12px; + font-weight: 600; + line-height: 1; cursor: pointer; transition: var(--transition-fast); } +.quick-chat-new-chat-btn { + background: var(--cta-bg); + border: 1px solid var(--cta-border); + color: var(--cta-text); +} + +.quick-chat-new-chat-btn:hover { + background: var(--cta-bg-hover); +} + +.quick-chat-new-chat-btn:disabled, +.quick-chat-new-chat-btn:disabled:hover { + background: var(--cta-bg); + opacity: 0.6; + cursor: not-allowed; +} + .quick-chat-mode-btn:hover { background: color-mix(in srgb, var(--todo) 10%, transparent); color: var(--text); @@ -648,6 +684,8 @@ } .quick-chat-panel-header-actions { + --quick-chat-header-control-size: calc(var(--space-xl) + var(--space-md)); + width: auto; flex-shrink: 0; justify-content: flex-end; diff --git a/packages/dashboard/app/components/QuickChatFAB.tsx b/packages/dashboard/app/components/QuickChatFAB.tsx index 539f4a273..bd4984bf1 100644 --- a/packages/dashboard/app/components/QuickChatFAB.tsx +++ b/packages/dashboard/app/components/QuickChatFAB.tsx @@ -12,7 +12,7 @@ import { import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; import type { Components } from "react-markdown"; -import { Eye, EyeOff, MessageSquare, Paperclip, Send, Square, Wrench, X } from "lucide-react"; +import { Eye, EyeOff, MessageSquare, Paperclip, Plus, Send, Square, Wrench, X } from "lucide-react"; import { fetchModels, type Agent, type ModelInfo } from "../api"; import { CustomModelDropdown } from "./CustomModelDropdown"; import { AgentMentionPopup } from "./AgentMentionPopup"; @@ -1546,15 +1546,45 @@ export function QuickChatFAB({ )}
- + {agents.length > 0 && ( +
+ + +
+ )}
- {agents.length > 0 && ( -
- - -
- )} - {chatMode === "agent" && agents.length > 0 && (
diff --git a/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx b/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx index f4d00584f..3ddf91219 100644 --- a/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx +++ b/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx @@ -190,6 +190,14 @@ describe("QuickChatFAB", () => { }); expect(screen.queryByTestId("quick-chat-mode-toggle")).toBeNull(); expect(screen.queryByTestId("quick-chat-agent-select")).toBeNull(); + + const headerActions = document.querySelector(".quick-chat-panel-header-actions"); + expect(headerActions).toBeTruthy(); + if (!headerActions) return; + expect(within(headerActions).queryByTestId("quick-chat-mode-toggle")).toBeNull(); + expect(within(headerActions).getByTestId("quick-chat-new-thread")).toBeDefined(); + expect(within(headerActions).getByTestId("quick-chat-close")).toBeDefined(); + expect(within(headerActions).getAllByRole("button")).toHaveLength(2); }); it("auto-selects configured default model when no agents exist and enables input", async () => { @@ -626,6 +634,30 @@ describe("QuickChatFAB", () => { }); }); + it("renders header toggle controls inside header actions with icon-only new chat button", async () => { + render(); + + fireEvent.click(screen.getByTestId("quick-chat-fab")); + + const headerActions = await waitFor(() => document.querySelector(".quick-chat-panel-header-actions")); + expect(headerActions).toBeTruthy(); + if (!headerActions) return; + + const scoped = within(headerActions); + expect(scoped.getByTestId("quick-chat-mode-toggle")).toBeDefined(); + const agentModeBtn = scoped.getByTestId("quick-chat-mode-agent"); + const modelModeBtn = scoped.getByTestId("quick-chat-mode-model"); + expect(agentModeBtn).toHaveTextContent("A"); + expect(modelModeBtn).toHaveTextContent("M"); + expect(agentModeBtn).toHaveAttribute("aria-label", "Switch to agent mode"); + expect(modelModeBtn).toHaveAttribute("aria-label", "Switch to model mode"); + + const newThreadBtn = scoped.getByTestId("quick-chat-new-thread"); + expect(within(newThreadBtn).queryByText("New chat")).toBeNull(); + expect(newThreadBtn.querySelector("svg")).toBeTruthy(); + expect(scoped.getByTestId("quick-chat-close")).toBeDefined(); + }); + it("new chat action creates a fresh model thread without changing the selected model", async () => { const existingModelSession: ChatSession = { id: "session-model-001",