diff --git a/packages/dashboard/app/components/ChatView.css b/packages/dashboard/app/components/ChatView.css index 8951564065..f1a510f0b8 100644 --- a/packages/dashboard/app/components/ChatView.css +++ b/packages/dashboard/app/components/ChatView.css @@ -1372,6 +1372,8 @@ } .chat-input-row { + --chat-input-control-size: calc(var(--space-lg) * 2.5); + display: flex; align-items: flex-end; gap: var(--space-sm); @@ -1480,8 +1482,8 @@ } .chat-input-send { - width: 36px; - height: 36px; + width: var(--chat-input-control-size); + min-height: var(--chat-input-control-size); border-radius: 50%; border: none; background: var(--accent); @@ -1499,8 +1501,8 @@ } .chat-input-stop { - width: 36px; - height: 36px; + width: var(--chat-input-control-size); + min-height: var(--chat-input-control-size); border-radius: var(--radius-md); border: none; background: color-mix(in srgb, var(--color-error) 15%, transparent); @@ -1869,13 +1871,17 @@ } /* primary touch targets per AGENTS.md convention */ - .chat-attach-btn, - .chat-input-send, - .chat-input-stop { + .chat-attach-btn { min-width: calc(var(--space-lg) * 2.25); min-height: calc(var(--space-lg) * 2.25); } + .chat-input-send, + .chat-input-stop { + min-width: var(--chat-input-control-size); + min-height: var(--chat-input-control-size); + } + .chat-attachment-preview { width: calc(var(--space-xl) * 2.5); height: calc(var(--space-xl) * 2.5); diff --git a/packages/dashboard/app/components/__tests__/ChatView.chat-input-autosize.test.tsx b/packages/dashboard/app/components/__tests__/ChatView.chat-input-autosize.test.tsx index dcc922e762..41270d9196 100644 --- a/packages/dashboard/app/components/__tests__/ChatView.chat-input-autosize.test.tsx +++ b/packages/dashboard/app/components/__tests__/ChatView.chat-input-autosize.test.tsx @@ -15,16 +15,22 @@ describe("ChatView chat input autosize", () => { expect(textareaRule?.[0]).toContain("overflow-y: hidden"); }); - it("keeps the stop button dimensions aligned with the send button", () => { + it("keeps the stop button dimensions aligned with the send button and textarea minimum", () => { + const rowRule = chatViewCss.match(/\.chat-input-row\s*\{[^}]*\}/); + const textareaRule = chatViewCss.match(/\.chat-input-textarea\s*\{[^}]*\}/); const sendRule = chatViewCss.match(/\.chat-input-send\s*\{[^}]*\}/); const stopRule = chatViewCss.match(/\.chat-input-stop\s*\{[^}]*\}/); + expect(rowRule).not.toBeNull(); + expect(textareaRule).not.toBeNull(); expect(sendRule).not.toBeNull(); expect(stopRule).not.toBeNull(); - expect(sendRule?.[0]).toContain("width: 36px"); - expect(sendRule?.[0]).toContain("height: 36px"); - expect(stopRule?.[0]).toContain("width: 36px"); - expect(stopRule?.[0]).toContain("height: 36px"); + expect(rowRule?.[0]).toContain("--chat-input-control-size: calc(var(--space-lg) * 2.5)"); + expect(textareaRule?.[0]).toContain("min-height: 40px"); + expect(sendRule?.[0]).toContain("width: var(--chat-input-control-size)"); + expect(sendRule?.[0]).toContain("min-height: var(--chat-input-control-size)"); + expect(stopRule?.[0]).toContain("width: var(--chat-input-control-size)"); + expect(stopRule?.[0]).toContain("min-height: var(--chat-input-control-size)"); }); it("clamps oversized textarea growth to the new max height", () => { diff --git a/packages/dashboard/vitest.config.ts b/packages/dashboard/vitest.config.ts index ffd541d67c..13c9b263a5 100644 --- a/packages/dashboard/vitest.config.ts +++ b/packages/dashboard/vitest.config.ts @@ -231,7 +231,7 @@ const qualityAppComponentBatchBTests = buildComponentQualityInclude(batchedQuali const qualityAppAppOnlyTests = ["app/components/__tests__/App.test.tsx"]; const qualityAppChatOnlyTests = ["app/components/__tests__/ChatView.test.tsx"]; const qualityAppSettingsOnlyTests = ["app/components/__tests__/SettingsModal.test.tsx"]; - +const quarantinedDashboardTests = ["app/components/__tests__/ChatView.rooms.test.tsx"]; const qualityApiTests = [ // Critical HTTP/server behavior: auth, task/project/settings mutation, @@ -318,6 +318,7 @@ export default defineConfig({ minWorkers: 1, fileParallelism: true, isolate: true, + exclude: quarantinedDashboardTests, // Dashboard route and integration-heavy suites can exceed the Vitest // 5s default under workspace-concurrent runs. testTimeout: 15_000, diff --git a/scripts/lib/test-quarantine.json b/scripts/lib/test-quarantine.json index 39eac9c428..89f8774fdc 100644 --- a/scripts/lib/test-quarantine.json +++ b/scripts/lib/test-quarantine.json @@ -1,4 +1,10 @@ { "$comment": "Flaky-test quarantine ledger (deletion ratchet — see AGENTS.md 'Flaky tests: quarantine on sight' and docs/testing.md 'Quarantine ledger and the deletion ratchet'). A test observed failing without a corresponding real bug is quarantined ON SIGHT: add an entry here AND a matching one-line `exclude` entry in that package's vitest config, in the same commit. Every entry needs a non-empty `reason` (link the failing run) and a `quarantinedAt` date — the entry expires 14 days later, at which point the test file is DELETED unless someone rescues it with evidence it catches real regressions plus a root-cause fix (never appeasement). There is deliberately no loader module and no automation around this file: it is a dated record, the vitest config exclude is the mechanism, and the sweep is policy executed by whoever touches the suite.", - "entries": [] + "entries": [ + { + "file": "packages/dashboard/app/components/__tests__/ChatView.rooms.test.tsx", + "reason": "FN-6103 workspace pnpm test observed flaky mobile visibility restore assertion: expected scrollTop 1180, received 300 in ChatView.rooms.test.tsx:805; unrelated to CSS-only composer button sizing change.", + "quarantinedAt": "2026-06-09" + } + ] }