Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/pr-screenshots/2554-android/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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.

65 changes: 34 additions & 31 deletions apps/mobile/test/conversation-row-layout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,38 +126,41 @@ function style(element: Element): Record<string, unknown> {
}

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;
Expand Down
Loading