Skip to content

Commit 7a53070

Browse files
committed
fix: align watch chrome aperture across clients
1 parent 49de816 commit 7a53070

11 files changed

Lines changed: 673 additions & 56 deletions

File tree

cli/XCWChromeRenderer.m

Lines changed: 469 additions & 47 deletions
Large diffs are not rendered by default.

client/src/api/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,10 @@ export interface ChromeProfile {
231231
screenY: number;
232232
screenWidth: number;
233233
screenHeight: number;
234+
contentX?: number;
235+
contentY?: number;
236+
contentWidth?: number;
237+
contentHeight?: number;
234238
cornerRadius: number;
235239
cornerRadii?: {
236240
topLeft?: number;

client/src/app/AppShell.tsx

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ import {
8484
buildShellRotationTransform,
8585
clampPan,
8686
clampZoom,
87+
computeChromeBackingRect,
8788
computeChromeScreenBorderRadius,
8889
computeChromeScreenRect,
8990
normalizeQuarterTurns,
@@ -149,6 +150,7 @@ const STREAM_TRANSPORT_VALUES = new Set<StreamTransport>([
149150
"webrtc",
150151
]);
151152
const MOBILE_VIEWPORT_MEDIA_QUERY = "(max-width: 600px)";
153+
const CHROME_RENDERER_ASSET_VERSION = "chrome-renderer-watch-bezel-inset-22";
152154
clearLegacyVolatileUiState();
153155

154156
interface StreamQualityResponse {
@@ -231,6 +233,10 @@ function buildChromeProfileAssetStamp(profile: ChromeProfile | null): string {
231233
profile.screenY,
232234
profile.screenWidth,
233235
profile.screenHeight,
236+
profile.contentX,
237+
profile.contentY,
238+
profile.contentWidth,
239+
profile.contentHeight,
234240
profile.cornerRadius,
235241
]
236242
.map(chromeStampNumber)
@@ -981,6 +987,7 @@ export function AppShell({
981987
selectedSimulator?.runtimeName,
982988
selectedSimulator?.udid,
983989
chromeGeometryStamp,
990+
CHROME_RENDERER_ASSET_VERSION,
984991
chromeHasInteractiveButtons ? "baked-buttons" : "no-buttons",
985992
chromeHasCrown ? "crown" : "no-crown",
986993
]
@@ -1755,10 +1762,17 @@ export function AppShell({
17551762
viewportChromeProfile,
17561763
effectiveDeviceNaturalSize,
17571764
);
1765+
const chromeScreenBackingRect = computeChromeBackingRect(
1766+
viewportChromeProfile,
1767+
);
17581768
const chromeScreenBorderRadius = computeChromeScreenBorderRadius(
17591769
viewportChromeProfile,
17601770
chromeScreenRect,
17611771
);
1772+
const chromeScreenBackingBorderRadius = computeChromeScreenBorderRadius(
1773+
viewportChromeProfile,
1774+
chromeScreenBackingRect,
1775+
);
17621776
const chromeScreenStyle =
17631777
viewportChromeProfile && chromeScreenRect
17641778
? ({
@@ -1788,6 +1802,16 @@ export function AppShell({
17881802
: {}),
17891803
} satisfies CSSProperties)
17901804
: null;
1805+
const chromeScreenBackingStyle =
1806+
viewportChromeProfile && chromeScreenBackingRect
1807+
? ({
1808+
left: `${(chromeScreenBackingRect.x / viewportChromeProfile.totalWidth) * 100}%`,
1809+
top: `${(chromeScreenBackingRect.y / viewportChromeProfile.totalHeight) * 100}%`,
1810+
width: `${(chromeScreenBackingRect.width / viewportChromeProfile.totalWidth) * 100}%`,
1811+
height: `${(chromeScreenBackingRect.height / viewportChromeProfile.totalHeight) * 100}%`,
1812+
borderRadius: chromeScreenBackingBorderRadius ?? "0",
1813+
} satisfies CSSProperties)
1814+
: null;
17911815
const screenOnlyStyle =
17921816
!viewportChromeProfile && chromeProfile && chromeProfile.screenWidth > 0
17931817
? isAndroidViewport
@@ -2804,6 +2828,7 @@ export function AppShell({
28042828
chromeProfile={viewportChromeProfile}
28052829
chromeRequired={chromeRequired}
28062830
chromeButtonsRenderedInChrome={chromeButtonsRenderedInChrome}
2831+
chromeScreenBackingStyle={chromeScreenBackingStyle}
28072832
chromeScreenStyle={viewportScreenStyle}
28082833
chromeUrl={chromeUrl}
28092834
chromeButtonUrl={chromeButtonUrl}

client/src/features/viewport/DeviceChrome.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ interface DeviceChromeProps {
1717
accessibilitySelectedId: string;
1818
chromeProfile: ChromeProfile | null;
1919
chromeButtonsRenderedInChrome: boolean;
20+
chromeScreenBackingStyle: CSSProperties | null;
2021
chromeScreenStyle: CSSProperties | null;
2122
chromeUrl: string;
2223
chromeButtonUrl: (button: string, pressed?: boolean) => string;
@@ -63,6 +64,7 @@ export function DeviceChrome({
6364
accessibilitySelectedId,
6465
chromeProfile,
6566
chromeButtonsRenderedInChrome,
67+
chromeScreenBackingStyle,
6668
chromeScreenStyle,
6769
chromeUrl,
6870
chromeButtonUrl,
@@ -120,6 +122,13 @@ export function DeviceChrome({
120122
draggable={false}
121123
src={chromeUrl}
122124
/>
125+
{chromeScreenBackingStyle ? (
126+
<div
127+
aria-hidden="true"
128+
className="device-screen-backing"
129+
style={chromeScreenBackingStyle}
130+
/>
131+
) : null}
123132
<ChromeButtonOverlay
124133
chromeButtonUrl={chromeButtonUrl}
125134
chromeProfile={chromeProfile}

client/src/features/viewport/SimulatorViewport.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface SimulatorViewportProps {
2121
chromeLoaded: boolean;
2222
chromeRequired: boolean;
2323
chromeButtonsRenderedInChrome: boolean;
24+
chromeScreenBackingStyle: CSSProperties | null;
2425
chromeScreenStyle: CSSProperties | null;
2526
chromeUrl: string;
2627
deviceFrameStyle: CSSProperties;
@@ -92,6 +93,7 @@ export function SimulatorViewport({
9293
chromeLoaded,
9394
chromeRequired,
9495
chromeButtonsRenderedInChrome,
96+
chromeScreenBackingStyle,
9597
chromeScreenStyle,
9698
chromeUrl,
9799
deviceFrameStyle,
@@ -202,6 +204,7 @@ export function SimulatorViewport({
202204
accessibilitySelectedId={accessibilitySelectedId}
203205
chromeProfile={chromeProfile}
204206
chromeButtonsRenderedInChrome={chromeButtonsRenderedInChrome}
207+
chromeScreenBackingStyle={chromeScreenBackingStyle}
205208
chromeScreenStyle={chromeScreenStyle}
206209
chromeUrl={chromeUrl}
207210
chromeButtonUrl={chromeButtonUrl}

client/src/features/viewport/viewportMath.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
buildShellRotationTransform,
55
clampPan,
66
clampZoom,
7+
computeChromeBackingRect,
78
computeChromeScreenBorderRadius,
89
computeChromeScreenRect,
910
computeFitScale,
@@ -88,6 +89,37 @@ describe("viewportMath", () => {
8889
});
8990
});
9091

92+
it("keeps watch display backing separate from stream content", () => {
93+
const profile = {
94+
contentHeight: 464.062,
95+
contentWidth: 381,
96+
contentX: 88.5,
97+
contentY: 58.469,
98+
cornerRadius: 135,
99+
screenHeight: 513,
100+
screenWidth: 422,
101+
screenX: 68,
102+
screenY: 34,
103+
totalHeight: 581,
104+
totalWidth: 573,
105+
};
106+
107+
expect(computeChromeBackingRect(profile)).toEqual({
108+
height: 513,
109+
width: 422,
110+
x: 68,
111+
y: 34,
112+
});
113+
expect(
114+
computeChromeScreenRect(profile, { width: 422, height: 514 }),
115+
).toEqual({
116+
height: 464.062,
117+
width: 381,
118+
x: 88.5,
119+
y: 58.469,
120+
});
121+
});
122+
91123
it("only rounds stream corners that touch the physical screen corners", () => {
92124
const profile = {
93125
cornerRadius: 40,

client/src/features/viewport/viewportMath.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,16 @@ export function mapDisplayedPointToNaturalOrientation(
8989
export function computeChromeScreenRect(
9090
chromeProfile: ChromeProfile | null,
9191
_deviceNaturalSize: Size | null,
92+
): ScreenRect | null {
93+
const contentRect = computeChromeContentRect(chromeProfile);
94+
if (contentRect) {
95+
return contentRect;
96+
}
97+
return computeChromeBackingRect(chromeProfile);
98+
}
99+
100+
export function computeChromeBackingRect(
101+
chromeProfile: ChromeProfile | null,
92102
): ScreenRect | null {
93103
if (!chromeProfile) {
94104
return null;
@@ -113,6 +123,30 @@ export function computeChromeScreenRect(
113123
};
114124
}
115125

126+
function computeChromeContentRect(
127+
chromeProfile: ChromeProfile | null,
128+
): ScreenRect | null {
129+
if (!chromeProfile) {
130+
return null;
131+
}
132+
if (
133+
!Number.isFinite(chromeProfile.contentX) ||
134+
!Number.isFinite(chromeProfile.contentY) ||
135+
!Number.isFinite(chromeProfile.contentWidth) ||
136+
!Number.isFinite(chromeProfile.contentHeight) ||
137+
(chromeProfile.contentWidth ?? 0) <= 0 ||
138+
(chromeProfile.contentHeight ?? 0) <= 0
139+
) {
140+
return null;
141+
}
142+
return {
143+
height: chromeProfile.contentHeight ?? 0,
144+
width: chromeProfile.contentWidth ?? 0,
145+
x: chromeProfile.contentX ?? 0,
146+
y: chromeProfile.contentY ?? 0,
147+
};
148+
}
149+
116150
export function computeChromeScreenBorderRadius(
117151
chromeProfile: ChromeProfile | null,
118152
screenRect: ScreenRect | null,
@@ -149,6 +183,27 @@ export function computeChromeScreenBorderRadius(
149183
const bottomRight = bottomTouches && rightTouches ? radius : 0;
150184
const bottomLeft = bottomTouches && leftTouches ? radius : 0;
151185

186+
if (
187+
topLeft === 0 &&
188+
topRight === 0 &&
189+
bottomRight === 0 &&
190+
bottomLeft === 0 &&
191+
chromeProfile.contentWidth &&
192+
chromeProfile.contentHeight &&
193+
Math.abs(screenRect.x - (chromeProfile.contentX ?? Number.NaN)) <=
194+
epsilon &&
195+
Math.abs(screenRect.y - (chromeProfile.contentY ?? Number.NaN)) <=
196+
epsilon &&
197+
Math.abs(screenRect.width - chromeProfile.contentWidth) <= epsilon &&
198+
Math.abs(screenRect.height - chromeProfile.contentHeight) <= epsilon
199+
) {
200+
const contentRadius = Math.max(
201+
0,
202+
Math.min(radius, screenRect.width / 2, screenRect.height / 2),
203+
);
204+
return `${contentRadius}px ${contentRadius}px ${contentRadius}px ${contentRadius}px`;
205+
}
206+
152207
return `${topLeft}px ${topRight}px ${bottomRight}px ${bottomLeft}px`;
153208
}
154209

client/src/styles/components.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,6 +2374,13 @@
23742374
z-index: 3;
23752375
}
23762376

2377+
.device-screen-backing {
2378+
position: absolute;
2379+
background: #000000;
2380+
pointer-events: none;
2381+
z-index: 0;
2382+
}
2383+
23772384
.device-chrome-button {
23782385
position: absolute;
23792386
display: block;

ios/SimDeckStudio/App/Models.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,10 @@ struct ChromeProfile: Hashable, Decodable, Sendable {
246246
let screenY: Double
247247
let screenWidth: Double
248248
let screenHeight: Double
249+
let contentX: Double?
250+
let contentY: Double?
251+
let contentWidth: Double?
252+
let contentHeight: Double?
249253
let cornerRadius: Double
250254
let chromeStyle: String?
251255
let hasScreenMask: Bool?
@@ -259,6 +263,10 @@ struct ChromeProfile: Hashable, Decodable, Sendable {
259263
screenY,
260264
screenWidth,
261265
screenHeight,
266+
contentX ?? 0,
267+
contentY ?? 0,
268+
contentWidth ?? 0,
269+
contentHeight ?? 0,
262270
cornerRadius
263271
]
264272
.map { value in

0 commit comments

Comments
 (0)