diff --git a/.github/pr-screenshots/2554-android/README.md b/.github/pr-screenshots/2554-android/README.md index 61d61170d8..843a51e5c1 100644 --- a/.github/pr-screenshots/2554-android/README.md +++ b/.github/pr-screenshots/2554-android/README.md @@ -13,7 +13,6 @@ Actual run: 2026-10-22, Android 16 arm64 physical phone, 1220 × 2712 screenshot - Preparation: 20 files / 163 tests and mobile typecheck passed. Separate baseline/candidate root builds passed; one sequential Metro and isolated QA Hosts. These source/build results are not native acceptance. - Official provider/model: `deepseek-official` / `deepseek-flash`, verified with actual phone composer → Host → visible reply, not API-only. USB loopback QA route does not prove tailnet or QR scanning. - ## PR2554 — sampled geometry improved; full acceptance remains PARTIAL Baseline and candidate share the same HarborBot/42 data for row captures. Default/intermediate1.3 and extra2.0 stress light/dark captured. Baseline2.0 places title, time and preview on separate lines. Candidate places title/time on the same horizontal line with preview/status below and real PR2552 trailing menu. At2.0 dark, title/time are still truncated; this is not a full-content-visibility pass. @@ -22,9 +21,6 @@ Actual OEM maximum was selected on the system font slider: label200% maps to And Live font changes produced Expo Router linking/navigation warnings in both sources. Warnings remain visible in published full-app crops, with no attribution to these PRs. Some warnings overlap bottom navigation. Long-title row opening, complete accessibility and iOS/Human acceptance remain untested. - - - Status/system-navigation pixels cropped for privacy. No generated/reconstructed UI. PNGs reviewed individually through labeled contact sheets; recording frames sampled every5s across their full selected interval. No personal notification or credential visible in reviewed frames. Public clips only contain designated QA journeys; raw originals remain private. This sampling is not frame-by-frame privacy certification. ## Restoration and exclusions @@ -36,4 +32,3 @@ Own Host/Metro processes, descendants, ports and recorders were stopped. Lease a Telemetry2570 remained BLOCKED: no independent production-egress isolation or dedicated synthetic container. No analytics configuration, interception, statistics-file mutation or personal opt-out operation occurred. Hermes2565, HostSettings2564, grid2574, Video and native splash2579 were excluded. Splash2579 is accepted for a future main batch containing merge55fe0fa36669e21d343bec7e417b2387d239118f and requires a new native APK; old binary/Metro cannot verify it. Public images are pixel crops removing status/navigation bars only, with hashes and source binding. Raw logs, identifiers, private profiles, XML and unreviewed frames remain private. Public publication status is tracked in the sole queue; local assets alone do not complete PR evidence delivery. - diff --git a/apps/mobile/test/conversation-row-layout.test.tsx b/apps/mobile/test/conversation-row-layout.test.tsx index 2a153dd852..8b3b7f474b 100644 --- a/apps/mobile/test/conversation-row-layout.test.tsx +++ b/apps/mobile/test/conversation-row-layout.test.tsx @@ -126,38 +126,41 @@ function style(element: Element): Record { } describe.each(['android', 'ios'])('Chats row layout contract on %s', (os) => { - it.each([1, 1.3, 2, 3.571])('keeps two lines at scale %s, including beside an action target', (scale) => { - device.os = os; - device.scale = scale; - for (const menu of [false, true]) { - const row = render('12:39', menu); - const source = row.name.parentElement!; - const header = source.parentElement!; - const content = header.parentElement!; - expect(style(header)).toMatchObject({ flexDirection: 'row', alignItems: 'center' }); - expect(style(content)).toMatchObject({ flex: 1, minWidth: 0 }); - expect(style(source)).toMatchObject({ flex: 1, minWidth: 0 }); - expect(style(row.name)).toMatchObject({ flexShrink: 1 }); - expect(style(source.children[1]!)).toMatchObject({ flexShrink: 0 }); - expect(source.getAttribute('aria-label')).toContain(title); - expect(row.name.dataset.ellipsis).toBe('tail'); - expect(row.time.parentElement).toBe(header); - expect(style(row.time)).toMatchObject({ flexShrink: 0 }); - expect(content.children).toHaveLength(2); - expect(row.preview.parentElement).toBe(content.children[1]); - expect(row.preview.parentElement!.querySelector('[data-status="working"]')).not.toBeNull(); - for (const text of [row.name, row.time, row.preview]) { - expect(text.dataset.lines).toBe('1'); - expect(text.dataset.scaling).not.toBe('false'); - expect(text.dataset.maximum).toBeUndefined(); - expect(text.dataset.fit).not.toBe('true'); + it.each([1, 1.3, 2, 3.571])( + 'keeps two lines at scale %s, including beside an action target', + (scale) => { + device.os = os; + device.scale = scale; + for (const menu of [false, true]) { + const row = render('12:39', menu); + const source = row.name.parentElement!; + const header = source.parentElement!; + const content = header.parentElement!; + expect(style(header)).toMatchObject({ flexDirection: 'row', alignItems: 'center' }); + expect(style(content)).toMatchObject({ flex: 1, minWidth: 0 }); + expect(style(source)).toMatchObject({ flex: 1, minWidth: 0 }); + expect(style(row.name)).toMatchObject({ flexShrink: 1 }); + expect(style(source.children[1]!)).toMatchObject({ flexShrink: 0 }); + expect(source.getAttribute('aria-label')).toContain(title); + expect(row.name.dataset.ellipsis).toBe('tail'); + expect(row.time.parentElement).toBe(header); + expect(style(row.time)).toMatchObject({ flexShrink: 0 }); + expect(content.children).toHaveLength(2); + expect(row.preview.parentElement).toBe(content.children[1]); + expect(row.preview.parentElement!.querySelector('[data-status="working"]')).not.toBeNull(); + for (const text of [row.name, row.time, row.preview]) { + expect(text.dataset.lines).toBe('1'); + expect(text.dataset.scaling).not.toBe('false'); + expect(text.dataset.maximum).toBeUndefined(); + expect(text.dataset.fit).not.toBe('true'); + } + expect(row.view.querySelector('button')!.getAttribute('aria-label')).toBe(label); + const target = row.view.querySelector('[aria-label="Reserved action target"]'); + expect(target === null).toBe(!menu); + if (target) expect(style(target).width).toBe(os === 'android' ? 48 : 44); } - expect(row.view.querySelector('button')!.getAttribute('aria-label')).toBe(label); - const target = row.view.querySelector('[aria-label="Reserved action target"]'); - expect(target === null).toBe(!menu); - if (target) expect(style(target).width).toBe(os === 'android' ? 48 : 44); - } - }); + }, + ); it('omits the timestamp slot without adding an empty metadata line', () => { device.os = os;