From 0b4a0f97f28c93ac47dec89a87b6a8ab6ee1150a Mon Sep 17 00:00:00 2001 From: 4sterisk <4sterisk@ymail.ne.jp> Date: Wed, 12 Aug 2026 02:23:50 +0900 Subject: [PATCH] =?UTF-8?q?480px=E4=BB=A5=E4=B8=8B=E3=81=AE=E7=8B=AD?= =?UTF-8?q?=E3=81=84=E7=94=BB=E9=9D=A2=E3=81=A7=E3=83=87=E3=82=A3=E3=83=AC?= =?UTF-8?q?=E3=82=AF=E3=83=88=E3=83=AA=E3=83=96=E3=83=A9=E3=82=A6=E3=82=B6?= =?UTF-8?q?=E3=81=AE=E5=8F=B3=E5=81=B4=E3=81=8C=E5=88=87=E3=82=8C=E3=82=8B?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit .directory-browser が margin: 0 auto + max-width のみで flex コンテナ内で fit-content 幅に縮み、#root の overflow: hidden により右側がクリップされて いた。width: 100% を追加して viewport に一致させる。 回帰テスト tests/narrow-browser.spec.js を追加 (480/375/320/280px で キー要素の右端が viewport 内に収まることを検証)。 --- client/src/styles/app.css | 6 +++++ tests/narrow-browser.spec.js | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/narrow-browser.spec.js diff --git a/client/src/styles/app.css b/client/src/styles/app.css index 2fdcb36..64c5064 100644 --- a/client/src/styles/app.css +++ b/client/src/styles/app.css @@ -386,6 +386,12 @@ body { height: 100%; display: flex; flex-direction: column; + /* margin: 0 auto だけだと flex コンテナ内で横方向に伸びず fit-content 幅に + なり、狭い viewport ではコンテンツ(ツールバー等)が max-content 幅の + まま右側へはみ出す(#root の overflow:hidden でクリップされ「右が切れる」)。 + width: 100% で viewport に一致させ、max-width + margin auto で広い画面では + 従来どおり中央寄せにする。 */ + width: 100%; max-width: 800px; margin: 0 auto; padding: 24px; diff --git a/tests/narrow-browser.spec.js b/tests/narrow-browser.spec.js new file mode 100644 index 0000000..1e86d21 --- /dev/null +++ b/tests/narrow-browser.spec.js @@ -0,0 +1,49 @@ +import { test, expect } from '@playwright/test'; +import { mkdtempSync, mkdirSync, writeFileSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; + +// Narrow viewports: the directory browser must never render wider than the +// viewport. #root/body carry overflow:hidden, so anything that sticks out +// past the right edge is clipped and unreachable (no page scroll to reach +// it). The regression this guards is `.directory-browser`'s +// `margin: 0 auto` collapsing it to a fit-content width inside the flex +// wrapper -- at 480px and below the toolbar/long names then blew out past +// the right edge, cutting off the size column, download buttons and the +// launch split button. +test.use({ viewport: { width: 320, height: 667 }, isMobile: true, hasTouch: true }); + +const KEY_SELECTORS = [ + '.directory-browser', + '.browser-toolbar', + '.open-split', + '.dir-item', + '.file-item', + '.file-download-btn', + '.file-size', + '.session-status', +]; + +for (const width of [480, 375, 320, 280]) { + test(`directory browser stays inside the ${width}px viewport`, async ({ page }) => { + const dir = mkdtempSync(join(tmpdir(), 'ccserver-narrow-')); + const longDir = join(dir, 'very-long-directory-name-that-goes-on-and-on-archive'); + const longFile = join(dir, 'very-long-file-name-with-many-words-documentation-notes.txt'); + mkdirSync(longDir); + writeFileSync(longFile, ''); + await page.setViewportSize({ width, height: 667 }); + await page.addInitScript((d) => localStorage.setItem('ccserver-last-dir', d), dir); + await page.goto('/'); + await page.waitForSelector('.dir-list'); + await expect(page.locator('.dir-item').first()).toBeVisible(); + + for (const sel of KEY_SELECTORS) { + for (const el of await page.locator(sel).all()) { + const box = await el.boundingBox(); + expect(box, `${sel} should have a box`).not.toBeNull(); + // The right edge of every key element must stay inside the viewport. + expect(box.x + box.width, `${sel} right edge clipped at ${width}px`).toBeLessThanOrEqual(width + 0.5); + } + } + }); +}