1import type { DevicePlatform } from "@t3tools/contracts";
2
3export interface DeviceShapeProfile {
4 readonly id: "ios-phone" | "ios-tablet" | "android-phone" | "android-tablet";
5 readonly bezel: number;
6 readonly bodyRadius: number;
7 readonly screenRadius: number;
8 readonly depth: number;
9 readonly backColor: number;
10 readonly buttons: ReadonlyArray<{
11 readonly edge: "left" | "right" | "top";
12 readonly offset: number;
13 readonly length: number;
14 }>;
15 readonly camera: {
16 readonly width: number;
17 readonly height: number;
18 readonly insetX: number;
19 readonly insetY: number;
20 readonly lensRadius: number;
21 readonly lenses: ReadonlyArray<readonly [number, number]>;
22 readonly flash: readonly [number, number] | null;
23 };
24}
25
26/** Original family silhouettes, rather than claims to reproduce individual hardware models. */
27export const IOS_PHONE_SHAPE: DeviceShapeProfile = {
28 id: "ios-phone",
29 bezel: 0.055,
30 bodyRadius: 0.15,
31 screenRadius: 0.105,
32 depth: 0.085,
33 backColor: 0x424b5d,
34 buttons: [
35 { edge: "right", offset: 0.35, length: 0.3 },
36 { edge: "left", offset: 0.48, length: 0.18 },
37 { edge: "left", offset: 0.22, length: 0.18 },
38 ],
39 camera: {
40 width: 0.39,
41 height: 0.44,
42 insetX: 0.25,
43 insetY: 0.29,
44 lensRadius: 0.068,
45 lenses: [
46 [-0.08, 0.095],
47 [0.08, -0.095],
48 ],
49 flash: [0.085, 0.11],
50 },
51};
52
53export const IOS_TABLET_SHAPE: DeviceShapeProfile = {
54 id: "ios-tablet",
55 bezel: 0.065,
56 bodyRadius: 0.105,
57 screenRadius: 0.045,
58 depth: 0.055,
59 backColor: 0x9ca5af,
60 buttons: [
61 { edge: "top", offset: 0.5, length: 0.15 },
62 { edge: "right", offset: 0.78, length: 0.13 },
63 { edge: "right", offset: 0.58, length: 0.13 },
64 ],
65 camera: {
66 width: 0.19,
67 height: 0.19,
68 insetX: 0.15,
69 insetY: 0.15,
70 lensRadius: 0.045,
71 lenses: [[0, 0]],
72 flash: null,
73 },
74};
75
76export const ANDROID_PHONE_SHAPE: DeviceShapeProfile = {
77 id: "android-phone",
78 bezel: 0.035,
79 bodyRadius: 0.115,
80 screenRadius: 0.08,
81 depth: 0.085,
82 backColor: 0x344449,
83 buttons: [
84 { edge: "right", offset: 0.2, length: 0.24 },
85 { edge: "right", offset: 0.65, length: 0.32 },
86 ],
87 camera: {
88 width: 0.24,
89 height: 0.47,
90 insetX: 0.18,
91 insetY: 0.29,
92 lensRadius: 0.056,
93 lenses: [
94 [0, 0.11],
95 [0, -0.11],
96 ],
97 flash: [0.1, 0],
98 },
99};
100
101const ANDROID_TABLET_SHAPE: DeviceShapeProfile = {
102 ...IOS_TABLET_SHAPE,
103 id: "android-tablet",
104 backColor: 0x697b80,
105};
106
107/** Names identify a family when available; wide unknown screens get a generic tablet shell. */
108export function resolveDeviceShape(options: {
109 platform: DevicePlatform;
110 name?: string;
111 portraitAspect: number;
112}): DeviceShapeProfile {
113 const name = options.name ?? "";
114 const namedTablet = /\b(ipad|tablet)\b/i.test(name);
115 const namedPhone = /\b(iphone|phone)\b/i.test(name);
116 const tablet = namedTablet || (!namedPhone && options.portraitAspect >= 0.6);
117 return options.platform === "ios"
118 ? tablet
119 ? IOS_TABLET_SHAPE
120 : IOS_PHONE_SHAPE
121 : tablet
122 ? ANDROID_TABLET_SHAPE
123 : ANDROID_PHONE_SHAPE;
124}