@appshoteditor/shot-dsl 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/builders.ts +3 -1
- package/src/compose.ts +31 -3
- package/src/device-frames.ts +40 -25
- package/src/types.ts +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@appshoteditor/shot-dsl",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "App Shot Editor layout DSL + device-frame geometry — framework-free building blocks for composing editable App Store screenshot layouts. Intended for use via a bundler (Vite, esbuild, etc.).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/builders.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BackgroundJSON, ColorStop, LayerJSON, ScreenLayersJSON, Template, TemplateRole } from './types';
|
|
1
|
+
import type { BackgroundJSON, ColorStop, DeviceClass, LayerJSON, ScreenLayersJSON, Template, TemplateRole } from './types';
|
|
2
2
|
import { CURRENT_SCHEMA_VERSION } from './types';
|
|
3
3
|
import { generateLayerId } from './validate';
|
|
4
4
|
|
|
@@ -176,11 +176,13 @@ export function makeScreen(opts: {
|
|
|
176
176
|
background?: BackgroundJSON;
|
|
177
177
|
canvasWidth?: number;
|
|
178
178
|
canvasHeight?: number;
|
|
179
|
+
deviceClass?: DeviceClass;
|
|
179
180
|
}): ScreenLayersJSON {
|
|
180
181
|
return {
|
|
181
182
|
schemaVersion: CURRENT_SCHEMA_VERSION,
|
|
182
183
|
canvasWidth: opts.canvasWidth ?? DEFAULT_CANVAS_WIDTH,
|
|
183
184
|
canvasHeight: opts.canvasHeight ?? DEFAULT_CANVAS_HEIGHT,
|
|
185
|
+
deviceClass: opts.deviceClass,
|
|
184
186
|
layers: opts.layers,
|
|
185
187
|
background: opts.background
|
|
186
188
|
};
|
package/src/compose.ts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import type { BackgroundJSON, Template } from './types';
|
|
2
2
|
import { makeTextLayer, makeScreen, makeTemplate } from './builders';
|
|
3
3
|
import { makeDeviceFrameLayers } from './frames';
|
|
4
|
+
import { getDeviceFrame, deviceClassForDeviceId } from './device-frames';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Editor-unit canvas dimensions for a device, by frame class — so a plan that mixes iPhone, iPad,
|
|
8
|
+
* Android and Mac screens gets the right aspect per screen (multi-device). Matches the app's
|
|
9
|
+
* Page Setup presets (phone 280×608, tablet 450×600, laptop/desktop 608×380). Export scales these
|
|
10
|
+
* up to the real App Store pixel sizes.
|
|
11
|
+
*/
|
|
12
|
+
function canvasDimsForDevice(deviceId: string): { width: number; height: number } {
|
|
13
|
+
switch (getDeviceFrame(deviceId)?.category) {
|
|
14
|
+
case 'tablet':
|
|
15
|
+
return { width: 450, height: 600 };
|
|
16
|
+
case 'laptop':
|
|
17
|
+
case 'desktop':
|
|
18
|
+
return { width: 608, height: 380 };
|
|
19
|
+
case 'phone':
|
|
20
|
+
default:
|
|
21
|
+
return { width: 280, height: 608 };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
4
24
|
|
|
5
25
|
/** One screen's worth of plan input (the skill decides these per benefit). */
|
|
6
26
|
export interface ComposeScreenPlan {
|
|
@@ -25,10 +45,15 @@ export interface ComposePlan {
|
|
|
25
45
|
* plan (which benefit, copy, device, palette); this turns it into valid DSL.
|
|
26
46
|
*/
|
|
27
47
|
export function composeTemplate(plan: ComposePlan): Template {
|
|
28
|
-
const canvasWidth = plan.canvasWidth ?? 280;
|
|
29
|
-
const canvasHeight = plan.canvasHeight ?? 600;
|
|
30
|
-
|
|
31
48
|
const screens = plan.screens.map((screen) => {
|
|
49
|
+
// Per-screen canvas dims: honor explicit plan-level dims (backward compatible — same as the
|
|
50
|
+
// old single-size behavior), else derive from the screen's device class so a mixed-device
|
|
51
|
+
// plan gets the correct aspect per screen.
|
|
52
|
+
const explicit = plan.canvasWidth != null || plan.canvasHeight != null;
|
|
53
|
+
const { width: canvasWidth, height: canvasHeight } = explicit
|
|
54
|
+
? { width: plan.canvasWidth ?? 280, height: plan.canvasHeight ?? 600 }
|
|
55
|
+
: canvasDimsForDevice(screen.deviceId);
|
|
56
|
+
|
|
32
57
|
const { screenshot, frame } = makeDeviceFrameLayers({
|
|
33
58
|
deviceId: screen.deviceId,
|
|
34
59
|
screenshotUrl: screen.screenshot.url,
|
|
@@ -60,6 +85,9 @@ export function composeTemplate(plan: ComposePlan): Template {
|
|
|
60
85
|
background: screen.background,
|
|
61
86
|
canvasWidth,
|
|
62
87
|
canvasHeight,
|
|
88
|
+
// Tag the device GROUP (multi-device) so a mixed plan lands as separate sidebar groups in
|
|
89
|
+
// the editor instead of relying on frame inference. Absent ⇒ editor infers it.
|
|
90
|
+
deviceClass: deviceClassForDeviceId(screen.deviceId) ?? undefined,
|
|
63
91
|
layers: [screenshot, frame, headline]
|
|
64
92
|
});
|
|
65
93
|
});
|
package/src/device-frames.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DeviceClass } from './types';
|
|
2
|
+
|
|
1
3
|
// =============================================================================
|
|
2
4
|
// DEVICE FRAME DEFINITIONS
|
|
3
5
|
// =============================================================================
|
|
@@ -36,7 +38,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
36
38
|
name: 'iPhone 16 Pro',
|
|
37
39
|
platform: 'ios',
|
|
38
40
|
category: 'phone',
|
|
39
|
-
frameAsset: '/devices/iphone-16-pro.
|
|
41
|
+
frameAsset: '/devices/iphone-16-pro.webp',
|
|
40
42
|
imageDimensions: { width: 1406, height: 2822 },
|
|
41
43
|
// Screen centered within frame: (1406-1212)/2=97, (2822-2618)/2=102
|
|
42
44
|
screenBounds: { x: 97, y: 100, width: 1212, height: 2624 },
|
|
@@ -47,7 +49,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
47
49
|
name: 'iPhone 16 Pro Max',
|
|
48
50
|
platform: 'ios',
|
|
49
51
|
category: 'phone',
|
|
50
|
-
frameAsset: '/devices/iphone-16-pro-max.
|
|
52
|
+
frameAsset: '/devices/iphone-16-pro-max.webp',
|
|
51
53
|
imageDimensions: { width: 1520, height: 3068 },
|
|
52
54
|
// Screen centered within frame: (1520-1310)/2=105, (3068-2846)/2=111
|
|
53
55
|
screenBounds: { x: 100, y: 100, width: 1320, height: 2870 },
|
|
@@ -58,7 +60,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
58
60
|
name: 'iPhone 16',
|
|
59
61
|
platform: 'ios',
|
|
60
62
|
category: 'phone',
|
|
61
|
-
frameAsset: '/devices/iphone-16.
|
|
63
|
+
frameAsset: '/devices/iphone-16.webp',
|
|
62
64
|
imageDimensions: { width: 1379, height: 2756 },
|
|
63
65
|
screenBounds: { x: 95, y: 98, width: 1189, height: 2563 },
|
|
64
66
|
cornerRadius: 118
|
|
@@ -68,7 +70,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
68
70
|
name: 'iPhone 16 Plus',
|
|
69
71
|
platform: 'ios',
|
|
70
72
|
category: 'phone',
|
|
71
|
-
frameAsset: '/devices/iphone-16-plus.
|
|
73
|
+
frameAsset: '/devices/iphone-16-plus.webp',
|
|
72
74
|
imageDimensions: { width: 1490, height: 2996 },
|
|
73
75
|
screenBounds: { x: 103, y: 106, width: 1284, height: 2786 },
|
|
74
76
|
cornerRadius: 128
|
|
@@ -78,7 +80,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
78
80
|
name: 'iPhone 17 Pro',
|
|
79
81
|
platform: 'ios',
|
|
80
82
|
category: 'phone',
|
|
81
|
-
frameAsset: '/devices/iphone-17-pro.
|
|
83
|
+
frameAsset: '/devices/iphone-17-pro.webp',
|
|
82
84
|
imageDimensions: { width: 1406, height: 2822 },
|
|
83
85
|
screenBounds: { x: 97, y: 100, width: 1212, height: 2624 },
|
|
84
86
|
cornerRadius: 120
|
|
@@ -88,7 +90,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
88
90
|
name: 'iPhone 17 Pro Max',
|
|
89
91
|
platform: 'ios',
|
|
90
92
|
category: 'phone',
|
|
91
|
-
frameAsset: '/devices/iphone-17-pro-max.
|
|
93
|
+
frameAsset: '/devices/iphone-17-pro-max.webp',
|
|
92
94
|
imageDimensions: { width: 1520, height: 3068 },
|
|
93
95
|
screenBounds: { x: 100, y: 100, width: 1320, height: 2870 },
|
|
94
96
|
cornerRadius: 140
|
|
@@ -98,7 +100,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
98
100
|
name: 'iPhone Air',
|
|
99
101
|
platform: 'ios',
|
|
100
102
|
category: 'phone',
|
|
101
|
-
frameAsset: '/devices/iphone-air.
|
|
103
|
+
frameAsset: '/devices/iphone-air.webp',
|
|
102
104
|
imageDimensions: { width: 1490, height: 2996 },
|
|
103
105
|
screenBounds: { x: 103, y: 106, width: 1284, height: 2786 },
|
|
104
106
|
cornerRadius: 128
|
|
@@ -112,7 +114,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
112
114
|
name: 'Google Pixel 9 Pro',
|
|
113
115
|
platform: 'android',
|
|
114
116
|
category: 'phone',
|
|
115
|
-
frameAsset: '/devices/pixel-9-pro.
|
|
117
|
+
frameAsset: '/devices/pixel-9-pro.webp',
|
|
116
118
|
imageDimensions: { width: 1620, height: 3136 },
|
|
117
119
|
screenBounds: { x: 112, y: 111, width: 1396, height: 2916 },
|
|
118
120
|
cornerRadius: 135
|
|
@@ -122,7 +124,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
122
124
|
name: 'Google Pixel 9 Pro XL',
|
|
123
125
|
platform: 'android',
|
|
124
126
|
category: 'phone',
|
|
125
|
-
frameAsset: '/devices/pixel-9-pro-xl.
|
|
127
|
+
frameAsset: '/devices/pixel-9-pro-xl.webp',
|
|
126
128
|
imageDimensions: { width: 1684, height: 3272 },
|
|
127
129
|
screenBounds: { x: 116, y: 116, width: 1452, height: 3043 },
|
|
128
130
|
cornerRadius: 140
|
|
@@ -136,7 +138,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
136
138
|
name: 'iPad Pro 13" (M4)',
|
|
137
139
|
platform: 'ios',
|
|
138
140
|
category: 'tablet',
|
|
139
|
-
frameAsset: '/devices/ipad-pro-13-m4.
|
|
141
|
+
frameAsset: '/devices/ipad-pro-13-m4.webp',
|
|
140
142
|
imageDimensions: { width: 2264, height: 2952 },
|
|
141
143
|
screenBounds: { x: 100, y: 100, width: 2064, height: 2752 },
|
|
142
144
|
cornerRadius: 40
|
|
@@ -146,7 +148,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
146
148
|
name: 'iPad Pro 11" (M4)',
|
|
147
149
|
platform: 'ios',
|
|
148
150
|
category: 'tablet',
|
|
149
|
-
frameAsset: '/devices/ipad-pro-11-m4.
|
|
151
|
+
frameAsset: '/devices/ipad-pro-11-m4.webp',
|
|
150
152
|
imageDimensions: { width: 1868, height: 2620 },
|
|
151
153
|
screenBounds: { x: 100, y: 116, width: 1668, height: 2388 },
|
|
152
154
|
cornerRadius: 40
|
|
@@ -156,7 +158,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
156
158
|
name: 'iPad Air 13"',
|
|
157
159
|
platform: 'ios',
|
|
158
160
|
category: 'tablet',
|
|
159
|
-
frameAsset: '/devices/ipad-air-13.
|
|
161
|
+
frameAsset: '/devices/ipad-air-13.webp',
|
|
160
162
|
imageDimensions: { width: 2248, height: 2932 },
|
|
161
163
|
screenBounds: { x: 100, y: 100, width: 2048, height: 2732 },
|
|
162
164
|
cornerRadius: 40
|
|
@@ -166,7 +168,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
166
168
|
name: 'iPad Air 11"',
|
|
167
169
|
platform: 'ios',
|
|
168
170
|
category: 'tablet',
|
|
169
|
-
frameAsset: '/devices/ipad-air-11.
|
|
171
|
+
frameAsset: '/devices/ipad-air-11.webp',
|
|
170
172
|
imageDimensions: { width: 1880, height: 2600 },
|
|
171
173
|
screenBounds: { x: 120, y: 120, width: 1640, height: 2360 },
|
|
172
174
|
cornerRadius: 40
|
|
@@ -176,7 +178,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
176
178
|
name: 'iPad mini 7',
|
|
177
179
|
platform: 'ios',
|
|
178
180
|
category: 'tablet',
|
|
179
|
-
frameAsset: '/devices/ipad-mini-7.
|
|
181
|
+
frameAsset: '/devices/ipad-mini-7.webp',
|
|
180
182
|
imageDimensions: { width: 1888, height: 2666 },
|
|
181
183
|
screenBounds: { x: 200, y: 200, width: 1488, height: 2266 },
|
|
182
184
|
cornerRadius: 40
|
|
@@ -190,7 +192,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
190
192
|
name: 'MacBook Air 13"',
|
|
191
193
|
platform: 'macos',
|
|
192
194
|
category: 'laptop',
|
|
193
|
-
frameAsset: '/devices/macbook-air-13.
|
|
195
|
+
frameAsset: '/devices/macbook-air-13.webp',
|
|
194
196
|
imageDimensions: { width: 3260, height: 2164 },
|
|
195
197
|
screenBounds: { x: 350, y: 306, width: 2560, height: 1608 },
|
|
196
198
|
cornerRadius: 20
|
|
@@ -200,7 +202,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
200
202
|
name: 'MacBook Air 13" (Menu Bar)',
|
|
201
203
|
platform: 'macos',
|
|
202
204
|
category: 'laptop',
|
|
203
|
-
frameAsset: '/devices/macbook-air-13-menu-bar.
|
|
205
|
+
frameAsset: '/devices/macbook-air-13-menu-bar.webp',
|
|
204
206
|
imageDimensions: { width: 3260, height: 2164 },
|
|
205
207
|
screenBounds: { x: 350, y: 312, width: 2560, height: 1602 },
|
|
206
208
|
cornerRadius: 20
|
|
@@ -210,7 +212,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
210
212
|
name: 'MacBook Air 15"',
|
|
211
213
|
platform: 'macos',
|
|
212
214
|
category: 'laptop',
|
|
213
|
-
frameAsset: '/devices/macbook-air-15.
|
|
215
|
+
frameAsset: '/devices/macbook-air-15.webp',
|
|
214
216
|
imageDimensions: { width: 3580, height: 2364 },
|
|
215
217
|
screenBounds: { x: 350, y: 306, width: 2880, height: 1808 },
|
|
216
218
|
cornerRadius: 20
|
|
@@ -220,7 +222,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
220
222
|
name: 'MacBook Air 15" (Menu Bar)',
|
|
221
223
|
platform: 'macos',
|
|
222
224
|
category: 'laptop',
|
|
223
|
-
frameAsset: '/devices/macbook-air-15-menu-bar.
|
|
225
|
+
frameAsset: '/devices/macbook-air-15-menu-bar.webp',
|
|
224
226
|
imageDimensions: { width: 3580, height: 2364 },
|
|
225
227
|
screenBounds: { x: 350, y: 308, width: 2880, height: 1806 },
|
|
226
228
|
cornerRadius: 20
|
|
@@ -230,7 +232,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
230
232
|
name: 'MacBook Pro 14"',
|
|
231
233
|
platform: 'macos',
|
|
232
234
|
category: 'laptop',
|
|
233
|
-
frameAsset: '/devices/macbook-pro-14.
|
|
235
|
+
frameAsset: '/devices/macbook-pro-14.webp',
|
|
234
236
|
imageDimensions: { width: 3944, height: 2564 },
|
|
235
237
|
screenBounds: { x: 461, y: 364, width: 3022, height: 1900 },
|
|
236
238
|
cornerRadius: 20
|
|
@@ -240,7 +242,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
240
242
|
name: 'MacBook Pro 14" (Menu Bar)',
|
|
241
243
|
platform: 'macos',
|
|
242
244
|
category: 'laptop',
|
|
243
|
-
frameAsset: '/devices/macbook-pro-14-menu-bar.
|
|
245
|
+
frameAsset: '/devices/macbook-pro-14-menu-bar.webp',
|
|
244
246
|
imageDimensions: { width: 3824, height: 2564 },
|
|
245
247
|
screenBounds: { x: 401, y: 374, width: 3022, height: 1890 },
|
|
246
248
|
cornerRadius: 20
|
|
@@ -250,7 +252,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
250
252
|
name: 'MacBook Pro 16"',
|
|
251
253
|
platform: 'macos',
|
|
252
254
|
category: 'laptop',
|
|
253
|
-
frameAsset: '/devices/macbook-pro-16.
|
|
255
|
+
frameAsset: '/devices/macbook-pro-16.webp',
|
|
254
256
|
imageDimensions: { width: 4340, height: 2860 },
|
|
255
257
|
screenBounds: { x: 442, y: 377, width: 3456, height: 2170 },
|
|
256
258
|
cornerRadius: 20
|
|
@@ -260,7 +262,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
260
262
|
name: 'MacBook Pro 16" (Menu Bar)',
|
|
261
263
|
platform: 'macos',
|
|
262
264
|
category: 'laptop',
|
|
263
|
-
frameAsset: '/devices/macbook-pro-16-menu-bar.
|
|
265
|
+
frameAsset: '/devices/macbook-pro-16-menu-bar.webp',
|
|
264
266
|
imageDimensions: { width: 4340, height: 2860 },
|
|
265
267
|
screenBounds: { x: 442, y: 389, width: 3456, height: 2158 },
|
|
266
268
|
cornerRadius: 20
|
|
@@ -274,7 +276,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
274
276
|
name: 'iMac 24"',
|
|
275
277
|
platform: 'macos',
|
|
276
278
|
category: 'desktop',
|
|
277
|
-
frameAsset: '/devices/imac-24.
|
|
279
|
+
frameAsset: '/devices/imac-24.webp',
|
|
278
280
|
imageDimensions: { width: 4880, height: 5720 },
|
|
279
281
|
screenBounds: { x: 200, y: 1600, width: 4480, height: 2520 },
|
|
280
282
|
cornerRadius: 0
|
|
@@ -284,7 +286,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
284
286
|
name: 'Studio Display',
|
|
285
287
|
platform: 'macos',
|
|
286
288
|
category: 'desktop',
|
|
287
|
-
frameAsset: '/devices/studio-display.
|
|
289
|
+
frameAsset: '/devices/studio-display.webp',
|
|
288
290
|
imageDimensions: { width: 5520, height: 4316 },
|
|
289
291
|
screenBounds: { x: 200, y: 200, width: 5120, height: 2880 },
|
|
290
292
|
cornerRadius: 0
|
|
@@ -294,7 +296,7 @@ export const deviceFrames: DeviceFrame[] = [
|
|
|
294
296
|
name: 'Pro Display XDR',
|
|
295
297
|
platform: 'macos',
|
|
296
298
|
category: 'desktop',
|
|
297
|
-
frameAsset: '/devices/pro-display-xdr.
|
|
299
|
+
frameAsset: '/devices/pro-display-xdr.webp',
|
|
298
300
|
imageDimensions: { width: 6416, height: 4865 },
|
|
299
301
|
screenBounds: { x: 200, y: 200, width: 6016, height: 3384 },
|
|
300
302
|
cornerRadius: 0
|
|
@@ -316,3 +318,16 @@ export function getDeviceFramesByPlatform(platform: DeviceFrame['platform']): De
|
|
|
316
318
|
export function getDeviceFramesByCategory(category: DeviceFrame['category']): DeviceFrame[] {
|
|
317
319
|
return deviceFrames.filter((d) => d.category === category);
|
|
318
320
|
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Map a device-frame id to its device GROUP (multi-device), using the frame's platform + category.
|
|
324
|
+
* Returns null for an unknown id. Kept in sync with the app's `device-groups.ts` mapping.
|
|
325
|
+
*/
|
|
326
|
+
export function deviceClassForDeviceId(deviceId: string): DeviceClass | null {
|
|
327
|
+
const frame = getDeviceFrame(deviceId);
|
|
328
|
+
if (!frame) return null;
|
|
329
|
+
if (frame.platform === 'ios') return frame.category === 'tablet' ? 'ipad_13' : 'iphone_6_9';
|
|
330
|
+
if (frame.platform === 'android') return 'android_phone';
|
|
331
|
+
if (frame.platform === 'macos' || frame.platform === 'windows') return 'macbook';
|
|
332
|
+
return null;
|
|
333
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -48,11 +48,19 @@ export interface BackgroundJSON {
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Device GROUP a screen belongs to (multi-device). Mirrors the app's `db.ts` union so a composed
|
|
53
|
+
* template can tag each screen and the editor buckets it into the right sidebar group on import.
|
|
54
|
+
*/
|
|
55
|
+
export type DeviceClass = 'iphone_6_9' | 'ipad_13' | 'android_phone' | 'macbook';
|
|
56
|
+
|
|
51
57
|
/** One screen (canvas) of a design. */
|
|
52
58
|
export interface ScreenLayersJSON {
|
|
53
59
|
schemaVersion: number;
|
|
54
60
|
canvasWidth?: number;
|
|
55
61
|
canvasHeight?: number;
|
|
62
|
+
/** Device group (multi-device); optional/additive — absent ⇒ the editor infers it. */
|
|
63
|
+
deviceClass?: DeviceClass;
|
|
56
64
|
layers: LayerJSON[];
|
|
57
65
|
background?: BackgroundJSON;
|
|
58
66
|
}
|