diff --git a/packages/dashboard/app/components/ModelOnboardingModal.tsx b/packages/dashboard/app/components/ModelOnboardingModal.tsx index 3ddeeff0e..886b0ae37 100644 --- a/packages/dashboard/app/components/ModelOnboardingModal.tsx +++ b/packages/dashboard/app/components/ModelOnboardingModal.tsx @@ -605,7 +605,7 @@ export function ModelOnboardingModal({ const [apiKeyInputs, setApiKeyInputs] = useState>({}); const [apiKeyErrors, setApiKeyErrors] = useState>({}); const [apiKeySuccess, setApiKeySuccess] = useState>({}); - const [customProviders, setCustomProviders] = useState([]); + const [customProviders, setCustomProviders] = useState([]); const [showCustomProviderForm, setShowCustomProviderForm] = useState(false); const [customProviderSaving, setCustomProviderSaving] = useState(false); const [customProviderError, setCustomProviderError] = useState(); diff --git a/packages/dashboard/app/components/QuickChatFAB.css b/packages/dashboard/app/components/QuickChatFAB.css index e4122e3d3..1b94cfce7 100644 --- a/packages/dashboard/app/components/QuickChatFAB.css +++ b/packages/dashboard/app/components/QuickChatFAB.css @@ -471,9 +471,8 @@ .quick-chat-input-wrapper { position: relative; display: flex; - align-items: center; - gap: var(--space-sm); flex: 1; + flex-direction: column; min-width: 0; border: 1px solid var(--border); border-radius: var(--radius-sm); @@ -481,6 +480,13 @@ padding: var(--space-xs); } +.quick-chat-input-row { + display: flex; + align-items: center; + gap: var(--space-sm); + min-width: 0; +} + .quick-chat-input-wrapper--dragover { border-color: var(--todo); background: color-mix(in srgb, var(--todo) 5%, var(--bg)); @@ -510,8 +516,8 @@ flex-shrink: 0; } -/* Only the outer send/stop action keeps the larger touch target. */ -.quick-chat-panel-input > button { +/* The send/stop action stays in the same inline composer row as attach + input. */ +.quick-chat-send-btn { min-width: calc(var(--space-lg) + var(--space-xl)); min-height: calc(var(--space-lg) + var(--space-xl)); width: calc(var(--space-lg) + var(--space-xl)); @@ -523,9 +529,10 @@ display: inline-flex; align-items: center; justify-content: center; + flex-shrink: 0; } -.quick-chat-panel-input > button:disabled { +.quick-chat-send-btn:disabled { opacity: 0.5; cursor: not-allowed; } @@ -643,7 +650,7 @@ height: calc(var(--space-lg) * 2 + var(--space-sm)); } - .quick-chat-panel-input > button { + .quick-chat-send-btn { min-width: calc(var(--space-lg) + var(--space-xl)); min-height: calc(var(--space-lg) + var(--space-xl)); } diff --git a/packages/dashboard/app/components/QuickChatFAB.tsx b/packages/dashboard/app/components/QuickChatFAB.tsx index bd4984bf1..48cc7cb04 100644 --- a/packages/dashboard/app/components/QuickChatFAB.tsx +++ b/packages/dashboard/app/components/QuickChatFAB.tsx @@ -1756,30 +1756,55 @@ export function QuickChatFAB({ event.target.value = ""; }} /> - - +
+ + + {isStreaming ? ( + + ) : ( + + )} +
)} - {isStreaming ? ( - - ) : ( - - )} )} diff --git a/packages/dashboard/app/components/SettingsModal.tsx b/packages/dashboard/app/components/SettingsModal.tsx index c865df287..e13909ff2 100644 --- a/packages/dashboard/app/components/SettingsModal.tsx +++ b/packages/dashboard/app/components/SettingsModal.tsx @@ -415,7 +415,7 @@ export function SettingsModal({ const [loginInstructions, setLoginInstructions] = useState>({}); const [apiKeyInputs, setApiKeyInputs] = useState>({}); const [apiKeyErrors, setApiKeyErrors] = useState>({}); - const [customProviders, setCustomProviders] = useState([]); + const [customProviders, setCustomProviders] = useState([]); const [customProviderEditing, setCustomProviderEditing] = useState(null); const [showCustomProviderForm, setShowCustomProviderForm] = useState(false); const [customProviderSaving, setCustomProviderSaving] = useState(false); @@ -833,7 +833,7 @@ export function SettingsModal({ } }, [customProviderEditing, loadCustomProviders]); - const handleDeleteCustomProvider = useCallback(async (provider: CustomProviderConfig) => { + const handleDeleteCustomProvider = useCallback(async (provider: CustomProvider) => { const ok = await confirm({ title: "Delete custom provider", message: `Delete custom provider '${provider.id}'?`, @@ -4711,7 +4711,23 @@ export function SettingsModal({ {provider.id}
- +
diff --git a/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx b/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx index 3ddf91219..93c88c905 100644 --- a/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx +++ b/packages/dashboard/app/components/__tests__/QuickChatFAB.test.tsx @@ -2309,6 +2309,29 @@ describe("QuickChatFAB", () => { clickSpy.mockRestore(); }); + it("keeps paperclip trigger and message input in the same composer row", async () => { + render(); + fireEvent.click(screen.getByTestId("quick-chat-fab")); + + const composerRow = await screen.findByTestId("quick-chat-input-row"); + const input = screen.getByTestId("quick-chat-input"); + const attachBtn = screen.getByTestId("quick-chat-attach-btn"); + const sendBtn = screen.getByTestId("quick-chat-send"); + + expect(composerRow).toContainElement(attachBtn); + expect(composerRow).toContainElement(input); + expect(composerRow).toContainElement(sendBtn); + + const fileInput = document.querySelector(".quick-chat-attachment-input") as HTMLInputElement; + const imageFile = new File(["img"], "photo.png", { type: "image/png" }); + fireEvent.change(fileInput, { target: { files: [imageFile] } }); + + await waitFor(() => { + expect(screen.getByTestId("quick-chat-attachment-preview-0")).toBeInTheDocument(); + expect(screen.getByTestId("quick-chat-send")).not.toBeDisabled(); + }); + }); + it("adds/removes previews for selected attachments and filters unsupported files", async () => { render(); fireEvent.click(screen.getByTestId("quick-chat-fab"));