





















@@ -116,6 +116,35 @@ function chatControlsHtml(opts: { agent?: boolean } = {}) {
116116 `;
117117}
118118119+function composerControlsHtml() {
120+return `
121+ <div class="agent-chat__composer-controls">
122+ <div class="chat-composer-model-control">
123+ <details class="chat-controls__session chat-controls__inline-select chat-controls__model">
124+ <summary class="chat-controls__inline-select-trigger" data-chat-composer-model="true" aria-label="Chat model">
125+ <span class="chat-controls__inline-select-label">Default model · Off</span>
126+ <span class="chat-controls__inline-select-icon">${iconSvg()}</span>
127+ </summary>
128+ <div class="chat-controls__inline-select-menu chat-controls__inline-select-menu--combined">
129+ <div class="chat-controls__combined-model-list">
130+ <button class="chat-controls__inline-select-option chat-controls__combined-model-option chat-controls__inline-select-option--selected">Default model</button>
131+ <button class="chat-controls__inline-select-option chat-controls__combined-model-option">gpt-5.5</button>
132+ <button class="chat-controls__inline-select-option chat-controls__combined-model-option">claude-sonnet-4-6</button>
133+ </div>
134+ </div>
135+ </details>
136+ </div>
137+ <div class="chat-settings-popover-wrapper">
138+ <button class="chat-settings-chip" type="button" aria-label="Chat settings">
139+ <span class="chat-settings-chip__icon">${iconSvg()}</span>
140+ <span class="chat-settings-chip__text">Chat settings</span>
141+ <span class="chat-settings-chip__chevron">${iconSvg()}</span>
142+ </button>
143+ </div>
144+ </div>
145+ `;
146+}
147+119148function chatHeaderControlsHtml(hidden = false) {
120149return `
121150 <main class="content content--chat" data-chat-header-responsive-fixture>
@@ -205,13 +234,13 @@ function chatHtml(opts: { sideResult?: boolean; singleAgent?: boolean } = {}) {
205234 </div>
206235 <div class="agent-chat__toolbar">
207236 <div class="agent-chat__toolbar-left">
237+ <button class="agent-chat__input-btn">${iconSvg()}</button>
208238 <button class="agent-chat__input-btn">${iconSvg()}</button>
209239 <button class="agent-chat__input-btn">${iconSvg()}</button>
210240 <span class="agent-chat__token-count">8</span>
211241 </div>
242+ ${composerControlsHtml()}
212243 <div class="agent-chat__toolbar-right">
213- <button class="btn btn--ghost">${iconSvg()}</button>
214- <button class="btn btn--ghost">${iconSvg()}</button>
215244 <button class="chat-send-btn">${iconSvg()}</button>
216245 </div>
217246 </div>
@@ -269,6 +298,18 @@ async function getTextContentRect(page: Page, selector: string) {
269298return rect;
270299}
271300301+function rectsOverlap(
302+first: Pick<ControlRect, "x" | "y" | "width" | "height">,
303+second: Pick<ControlRect, "x" | "y" | "width" | "height">,
304+) {
305+return (
306+first.x < second.x + second.width &&
307+first.x + first.width > second.x &&
308+first.y < second.y + second.height &&
309+first.y + first.height > second.y
310+);
311+}
312+272313async function openHeaderFixture(width: number, height: number, opts: { hidden?: boolean } = {}) {
273314const page = await browser.newPage({ viewport: { width, height } });
274315await page.setContent(
@@ -525,6 +566,64 @@ describeBrowserLayout("chat responsive browser layout", () => {
525566}
526567});
527568569+it.each([
570+[320, 568],
571+[393, 852],
572+[568, 320],
573+] as const)(
574+"keeps current composer model, settings, and send controls from overlapping at %sx%s",
575+async (width, height) => {
576+const page = await openFixture(width, height);
577+try {
578+await expectNoHorizontalOverflow(page);
579+const controls = await page.evaluate(() => {
580+const rectFor = (selector: string) => {
581+const node = document.querySelector(selector) as HTMLElement | null;
582+if (!node) {
583+return null;
584+}
585+const rect = node.getBoundingClientRect();
586+return {
587+x: rect.x,
588+y: rect.y,
589+width: rect.width,
590+height: rect.height,
591+display: getComputedStyle(node).display,
592+};
593+};
594+return {
595+input: rectFor(".agent-chat__input"),
596+left: rectFor(".agent-chat__toolbar-left"),
597+model: rectFor(".chat-composer-model-control"),
598+settings: rectFor(".chat-settings-chip"),
599+settingsLabel: rectFor(".chat-settings-chip__text"),
600+send: rectFor(".chat-send-btn"),
601+};
602+});
603+604+const input = expectControlRect(controls.input, "composer");
605+const left = expectControlRect(controls.left, "composer left controls");
606+const model = expectControlRect(controls.model, "composer model control");
607+const settings = expectControlRect(controls.settings, "composer settings control");
608+const send = expectControlRect(controls.send, "composer send control");
609+const settingsLabel = expectControlRect(controls.settingsLabel, "settings label");
610+611+for (const control of [left, model, settings, send]) {
612+expect(control.x).toBeGreaterThanOrEqual(input.x - 1);
613+expect(control.x + control.width).toBeLessThanOrEqual(input.x + input.width + 1);
614+}
615+expect(rectsOverlap(model, settings)).toBe(false);
616+expect(rectsOverlap(model, send)).toBe(false);
617+expect(rectsOverlap(settings, send)).toBe(false);
618+expect(settings.width).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
619+expect(settings.height).toBeGreaterThanOrEqual(TOUCH_TARGET_MIN_PX);
620+expect(settingsLabel.display).toBe("none");
621+} finally {
622+await page.close();
623+}
624+},
625+);
626+528627it("uses the compact mobile grid when the agent filter is not rendered", async () => {
529628const page = await openFixture(320, 568, { singleAgent: true });
530629try {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。