diff --git a/plugins/web-ui/src/shell.css b/plugins/web-ui/src/shell.css index 90c1c9382..56c54dfb9 100644 --- a/plugins/web-ui/src/shell.css +++ b/plugins/web-ui/src/shell.css @@ -2375,15 +2375,16 @@ a.chat-row-open { overscroll-behavior: contain; } .composer-approval { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - align-items: start; + display: flex; + flex-direction: column; + align-items: stretch; gap: 12px; min-height: 48px; padding: 4px 4px 8px; } .composer-approval-copy { display: grid; + width: 100%; gap: 5px; min-width: 0; } @@ -2397,6 +2398,7 @@ a.chat-row-open { max-width: 100%; } .composer-approval .approval-actions { + width: 100%; justify-content: flex-end; } .composer-toolbar { @@ -4402,9 +4404,6 @@ a.chat-row-open { .message-bubble { max-width: 88%; } - .composer-approval { - grid-template-columns: 1fr; - } .composer-approval .approval-actions { justify-content: flex-start; } @@ -4469,6 +4468,7 @@ a.chat-row-open { } .composer-approval .approval-btn { width: 100%; + min-height: 44px; } .composer-toolbar { flex-wrap: wrap; diff --git a/plugins/web-ui/test/pane-approval-overflow.test.ts b/plugins/web-ui/test/pane-approval-overflow.test.ts index fb8ea57cf..ef697a79f 100644 --- a/plugins/web-ui/test/pane-approval-overflow.test.ts +++ b/plugins/web-ui/test/pane-approval-overflow.test.ts @@ -5,6 +5,25 @@ import test from "node:test"; const css = readFileSync(new URL("../src/shell.css", import.meta.url), "utf8"); const chat = readFileSync(new URL("../src/chat.ts", import.meta.url), "utf8"); +test("approval details use the full row above their actions", () => { + const approval = css.match(/\.composer-approval \{[^}]*\}/)?.[0] ?? ""; + const copy = css.match(/\.composer-approval-copy \{[^}]*\}/)?.[0] ?? ""; + const actions = css.match(/\.composer-approval \.approval-actions \{[^}]*\}/)?.[0] ?? ""; + + assert.match(approval, /display: flex;/); + assert.match(approval, /flex-direction: column;/, "actions belong below the approval details at every width"); + assert.match(copy, /width: 100%;/, "approval details should use the full available row"); + assert.match(actions, /width: 100%;/, "the action row should not compete with the details for width"); +}); + +test("phone approval actions use full-width touch targets", () => { + const phone = css.match(/@media \(max-width: 560px\) \{[\s\S]*?\n\}/)?.[0] ?? ""; + const button = phone.match(/\.composer-approval \.approval-btn \{[^}]*\}/)?.[0] ?? ""; + + assert.match(button, /width: 100%;/); + assert.match(button, /min-height: 44px;/); +}); + test("approval panel caps to the pane viewport and scrolls instead of clipping", () => { const panel = css.match(/\.composer-approval-panel \{[^}]*\}/)?.[0] ?? ""; assert.match(panel, /max-height: calc\(100dvh - \d+px\);/, "panel needs a viewport-relative height cap");