@vosjs/studio-core 0.40.3 → 0.41.1
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/README.md +1 -1
- package/dist/index.d.ts +14 -43
- package/dist/index.js +21 -330
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,7 +45,7 @@ The shared layers lower as one engine `stack` entry, `STUDIO_ENTRY_ID` (`'vosso.
|
|
|
45
45
|
|
|
46
46
|
## Destinations
|
|
47
47
|
|
|
48
|
-
`DESTINATIONS` is the table of
|
|
48
|
+
`DESTINATIONS` is the table of video destinations (a YouTube demo, an X feed cut, a vertical cut for Shorts and LinkedIn, a GitHub README loop) with exact pixel sizes, byte and duration ceilings and a word policy, generated from `channel-specs.json` in `@vosjs/cli` and hash-gated by a test. `destinationById`, `destinationsForChannel`, `exportSizeFor(ratio, resolution)` and `resolveExportSize` size an export; `ExportResolution` is `720p | 1080p | 2k | 4k`.
|
|
49
49
|
|
|
50
50
|
## Exports by area
|
|
51
51
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1100,8 +1100,8 @@ interface FrameStyle {
|
|
|
1100
1100
|
* How footage meets an off-ratio frame. 'contain' (default) fits the
|
|
1101
1101
|
* whole card inside the padded area, letterboxing onto the background;
|
|
1102
1102
|
* 'cover' makes the padded area the card and cover-fills it with footage,
|
|
1103
|
-
* cropped around `focus` — what a
|
|
1104
|
-
*
|
|
1103
|
+
* cropped around `focus` — what a 9:16 cut of 16:9 footage demands
|
|
1104
|
+
* ("fill the region"). Absent = contain; every existing doc is
|
|
1105
1105
|
* byte-identical.
|
|
1106
1106
|
*/
|
|
1107
1107
|
fit?: 'contain' | 'cover';
|
|
@@ -1816,8 +1816,8 @@ interface ProjectDoc {
|
|
|
1816
1816
|
media?: Media[];
|
|
1817
1817
|
/**
|
|
1818
1818
|
* The frame that stands for this document (OUTPUT seconds): the cover the
|
|
1819
|
-
* shelf shows
|
|
1820
|
-
*
|
|
1819
|
+
* shelf shows. Absent, `docStillTime` derives it: the document's own
|
|
1820
|
+
* last freeze (the rest a closing hold ends on), else the hero
|
|
1821
1821
|
* moment after the opening has entered. A template never sets it.
|
|
1822
1822
|
*/
|
|
1823
1823
|
still?: number;
|
|
@@ -3857,8 +3857,8 @@ declare function wrapProgramLength(source: string): string;
|
|
|
3857
3857
|
declare function lowerProgramDoc(doc: ProgramAnchorDoc, opts?: LowerProgramOptions): LoweredComposition;
|
|
3858
3858
|
|
|
3859
3859
|
/**
|
|
3860
|
-
* A destination is where a release's
|
|
3861
|
-
* doors registry's "a door is what you bring". One row per channel
|
|
3860
|
+
* A destination is where a release's video goes — the output twin of the
|
|
3861
|
+
* doors registry's "a door is what you bring". One row per channel video,
|
|
3862
3862
|
* derived from the verified channel specs; `vos deliver` loops these and
|
|
3863
3863
|
* the kit manifest records them.
|
|
3864
3864
|
*/
|
|
@@ -3868,58 +3868,29 @@ interface Destination {
|
|
|
3868
3868
|
channel: string;
|
|
3869
3869
|
asset: string;
|
|
3870
3870
|
label: string;
|
|
3871
|
-
kind: 'video' | 'still' | 'still-set';
|
|
3872
3871
|
/** Reduced aspect ratio, the exportSizeFor convention. */
|
|
3873
3872
|
ratio: string;
|
|
3874
3873
|
px: {
|
|
3875
3874
|
w: number;
|
|
3876
3875
|
h: number;
|
|
3877
3876
|
};
|
|
3878
|
-
/** still-set only: how many the channel takes. */
|
|
3879
|
-
count?: {
|
|
3880
|
-
min: number;
|
|
3881
|
-
max: number;
|
|
3882
|
-
};
|
|
3883
|
-
/**
|
|
3884
|
-
* Image genre: 'screenshot' = real UI from the take (store policy demands
|
|
3885
|
-
* real UX); 'card' = a COMPOSED cover — rendered from the maker's poster
|
|
3886
|
-
* program when `vos deliver --poster` has one.
|
|
3887
|
-
*/
|
|
3888
|
-
genre?: 'screenshot' | 'card';
|
|
3889
3877
|
minSeconds?: number;
|
|
3890
3878
|
maxSeconds?: number;
|
|
3891
3879
|
maxBytes?: number;
|
|
3892
3880
|
/** The format the kit renders. */
|
|
3893
|
-
format: 'mp4'
|
|
3881
|
+
format: 'mp4';
|
|
3894
3882
|
/** What the channel accepts, in the spec's own words. */
|
|
3895
3883
|
accepts: string;
|
|
3896
|
-
/** How footage meets an off-ratio frame (a still fills, a video letterboxes). */
|
|
3897
|
-
fit: 'contain' | 'cover';
|
|
3898
3884
|
/**
|
|
3899
|
-
* The destination's word policy: 'none' = the picture carries it alone
|
|
3900
|
-
*
|
|
3901
|
-
*
|
|
3885
|
+
* The destination's word policy: 'none' = the picture carries it alone
|
|
3886
|
+
* (a README loop, so the beat captions drop), 'allowed', 'expected' = a
|
|
3887
|
+
* cut that should say something.
|
|
3902
3888
|
*/
|
|
3903
3889
|
text: 'none' | 'allowed' | 'expected';
|
|
3904
|
-
/**
|
|
3905
|
-
* The rect, as fractions of the frame, every text box must sit inside:
|
|
3906
|
-
* the platform's own chrome or crop covers the rest.
|
|
3907
|
-
*/
|
|
3908
|
-
safe: {
|
|
3909
|
-
x: number;
|
|
3910
|
-
y: number;
|
|
3911
|
-
w: number;
|
|
3912
|
-
h: number;
|
|
3913
|
-
};
|
|
3914
|
-
/**
|
|
3915
|
-
* The poster template a card destination renders from by default (the
|
|
3916
|
-
* family that ships with the CLI); absent on screenshots and videos.
|
|
3917
|
-
*/
|
|
3918
|
-
template?: string;
|
|
3919
3890
|
notes: string;
|
|
3920
3891
|
}
|
|
3921
3892
|
declare const CHANNEL_SPECS_VERIFIED = "2026-08-04";
|
|
3922
|
-
declare const CHANNEL_SPECS_HASH = "
|
|
3893
|
+
declare const CHANNEL_SPECS_HASH = "0f12781634495af6af1b49845009f04810d75829cfb0088dbf903d136e330f25";
|
|
3923
3894
|
declare const DESTINATIONS: Destination[];
|
|
3924
3895
|
declare function destinationById(id: string): Destination | undefined;
|
|
3925
3896
|
declare function destinationsForChannel(channel: string): Destination[];
|
|
@@ -4346,9 +4317,9 @@ declare function ownFreezes(doc: ProjectDoc): FreezeSpan[];
|
|
|
4346
4317
|
/** The still a document with nothing to say gets: the old default. */
|
|
4347
4318
|
declare const STILL_DEFAULT = 0.5;
|
|
4348
4319
|
/**
|
|
4349
|
-
* The output moment that STANDS for a document: its cover on the shelf
|
|
4350
|
-
*
|
|
4351
|
-
* fleet thumbnail,
|
|
4320
|
+
* The output moment that STANDS for a document: its cover on the shelf.
|
|
4321
|
+
* One ladder, one derivation, read by every surface that takes a cover
|
|
4322
|
+
* (the fleet thumbnail, `vos frames --at-still`); a template can
|
|
4352
4323
|
* never move it, because everything a template lays is stamped `from`.
|
|
4353
4324
|
*
|
|
4354
4325
|
* 1. `doc.still`, the author's own choice, clamped to the output.
|
package/dist/index.js
CHANGED
|
@@ -5061,26 +5061,32 @@ var STUDIO_SETUP = `async (ctx) => {
|
|
|
5061
5061
|
fontCache.set(oc.asset.url, font)
|
|
5062
5062
|
} catch (e) { console.warn('[voila] typeface failed to load', oc.asset.url, e) }
|
|
5063
5063
|
}
|
|
5064
|
-
// Audio clips (music/SFX):
|
|
5065
|
-
//
|
|
5066
|
-
//
|
|
5064
|
+
// Audio clips (music/SFX): start decoding into the shared cache now, but
|
|
5065
|
+
// never hold the first frame for it. Playback starts on a press, so the
|
|
5066
|
+
// decodes almost always land first; one that lands later clears the
|
|
5067
|
+
// scheduler's signature and the next playing frame schedules it at the
|
|
5068
|
+
// right offset (the onFrame path does the same for clips added later).
|
|
5069
|
+
// Exports never read this cache: they mix offline. The AudioContext starts
|
|
5070
|
+
// suspended (autoplay policy); onFrame resumes it on the first play.
|
|
5071
|
+
// Failures degrade to a silent clip.
|
|
5067
5072
|
const clips = ctx.data.audio || []
|
|
5068
5073
|
if (clips.length && window.AudioContext) {
|
|
5069
5074
|
const actx = ns.audioCtx || (ns.audioCtx = new window.AudioContext())
|
|
5070
5075
|
const bufs = ns.audioBuffers || (ns.audioBuffers = new Map())
|
|
5071
5076
|
const pend = ns.audioPending || (ns.audioPending = new Set())
|
|
5072
|
-
|
|
5073
|
-
if (bufs.has(c.key) || pend.has(c.key))
|
|
5077
|
+
for (const c of clips) {
|
|
5078
|
+
if (bufs.has(c.key) || pend.has(c.key)) continue
|
|
5074
5079
|
pend.add(c.key)
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5080
|
+
fetch(c.key)
|
|
5081
|
+
.then((res) => res.arrayBuffer())
|
|
5082
|
+
.then((ab) => actx.decodeAudioData(ab))
|
|
5083
|
+
.then((b) => {
|
|
5084
|
+
bufs.set(c.key, b)
|
|
5085
|
+
if (ns.audioSched) ns.audioSched.sig = ''
|
|
5086
|
+
})
|
|
5087
|
+
.catch((e) => console.warn('[voila] audio decode failed', c.key, e))
|
|
5088
|
+
.then(() => pend.delete(c.key))
|
|
5089
|
+
}
|
|
5084
5090
|
}
|
|
5085
5091
|
}`;
|
|
5086
5092
|
var STUDIO_CONTENT = `(ctx) => {
|
|
@@ -10278,14 +10284,13 @@ function lowerProgramDoc(doc, opts = {}) {
|
|
|
10278
10284
|
|
|
10279
10285
|
// src/destinations.ts
|
|
10280
10286
|
var CHANNEL_SPECS_VERIFIED = "2026-08-04";
|
|
10281
|
-
var CHANNEL_SPECS_HASH = "
|
|
10287
|
+
var CHANNEL_SPECS_HASH = "0f12781634495af6af1b49845009f04810d75829cfb0088dbf903d136e330f25";
|
|
10282
10288
|
var DESTINATIONS = [
|
|
10283
10289
|
{
|
|
10284
10290
|
id: "youtube-main-demo",
|
|
10285
10291
|
channel: "youtube",
|
|
10286
10292
|
asset: "main-demo",
|
|
10287
10293
|
label: "YouTube demo",
|
|
10288
|
-
kind: "video",
|
|
10289
10294
|
ratio: "16:9",
|
|
10290
10295
|
px: {
|
|
10291
10296
|
w: 1920,
|
|
@@ -10295,14 +10300,7 @@ var DESTINATIONS = [
|
|
|
10295
10300
|
maxSeconds: 120,
|
|
10296
10301
|
format: "mp4",
|
|
10297
10302
|
accepts: "mp4 (H.264+AAC)",
|
|
10298
|
-
fit: "contain",
|
|
10299
10303
|
text: "allowed",
|
|
10300
|
-
safe: {
|
|
10301
|
-
x: 0.05,
|
|
10302
|
-
y: 0.08,
|
|
10303
|
-
w: 0.9,
|
|
10304
|
-
h: 0.84
|
|
10305
|
-
},
|
|
10306
10304
|
notes: "Captions burned in. The same public upload serves the Chrome Web Store promo video and Product Hunt video (both take YouTube URLs only)."
|
|
10307
10305
|
},
|
|
10308
10306
|
{
|
|
@@ -10310,7 +10308,6 @@ var DESTINATIONS = [
|
|
|
10310
10308
|
channel: "x",
|
|
10311
10309
|
asset: "feed-cut",
|
|
10312
10310
|
label: "X feed cut",
|
|
10313
|
-
kind: "video",
|
|
10314
10311
|
ratio: "16:9",
|
|
10315
10312
|
px: {
|
|
10316
10313
|
w: 1920,
|
|
@@ -10321,14 +10318,7 @@ var DESTINATIONS = [
|
|
|
10321
10318
|
maxBytes: 536870912,
|
|
10322
10319
|
format: "mp4",
|
|
10323
10320
|
accepts: "mp4 (H.264+AAC ONLY \u2014 HEVC/VP9/AV1 rejected)",
|
|
10324
|
-
fit: "contain",
|
|
10325
10321
|
text: "allowed",
|
|
10326
|
-
safe: {
|
|
10327
|
-
x: 0.05,
|
|
10328
|
-
y: 0.08,
|
|
10329
|
-
w: 0.9,
|
|
10330
|
-
h: 0.84
|
|
10331
|
-
},
|
|
10332
10322
|
notes: "30\u201360s plays best; 140s/512MB is the free-tier ceiling. 1:1 also allowed. Native upload, never a link post."
|
|
10333
10323
|
},
|
|
10334
10324
|
{
|
|
@@ -10336,7 +10326,6 @@ var DESTINATIONS = [
|
|
|
10336
10326
|
channel: "shorts-linkedin",
|
|
10337
10327
|
asset: "vertical-cut",
|
|
10338
10328
|
label: "Shorts / LinkedIn vertical cut",
|
|
10339
|
-
kind: "video",
|
|
10340
10329
|
ratio: "9:16",
|
|
10341
10330
|
px: {
|
|
10342
10331
|
w: 1080,
|
|
@@ -10346,14 +10335,7 @@ var DESTINATIONS = [
|
|
|
10346
10335
|
maxSeconds: 90,
|
|
10347
10336
|
format: "mp4",
|
|
10348
10337
|
accepts: "mp4 (H.264+AAC)",
|
|
10349
|
-
fit: "contain",
|
|
10350
10338
|
text: "expected",
|
|
10351
|
-
safe: {
|
|
10352
|
-
x: 0.0833,
|
|
10353
|
-
y: 0.1979,
|
|
10354
|
-
w: 0.8333,
|
|
10355
|
-
h: 0.6042
|
|
10356
|
-
},
|
|
10357
10339
|
notes: "Critical text inside a centered ~900\xD71160 safe zone \u2014 platform chrome covers the rest. Serves YouTube Shorts and LinkedIn native vertical."
|
|
10358
10340
|
},
|
|
10359
10341
|
{
|
|
@@ -10361,7 +10343,6 @@ var DESTINATIONS = [
|
|
|
10361
10343
|
channel: "github",
|
|
10362
10344
|
asset: "readme-loop",
|
|
10363
10345
|
label: "GitHub README loop",
|
|
10364
|
-
kind: "video",
|
|
10365
10346
|
ratio: "16:9",
|
|
10366
10347
|
px: {
|
|
10367
10348
|
w: 1920,
|
|
@@ -10372,298 +10353,8 @@ var DESTINATIONS = [
|
|
|
10372
10353
|
maxBytes: 10485760,
|
|
10373
10354
|
format: "mp4",
|
|
10374
10355
|
accepts: "mp4 (H.264)",
|
|
10375
|
-
fit: "contain",
|
|
10376
10356
|
text: "none",
|
|
10377
|
-
safe: {
|
|
10378
|
-
x: 0.05,
|
|
10379
|
-
y: 0.08,
|
|
10380
|
-
w: 0.9,
|
|
10381
|
-
h: 0.84
|
|
10382
|
-
},
|
|
10383
10357
|
notes: "\u226410MB is the free-plan attachment ceiling; two-pass target bitrate from duration."
|
|
10384
|
-
},
|
|
10385
|
-
{
|
|
10386
|
-
id: "youtube-thumbnail",
|
|
10387
|
-
channel: "youtube",
|
|
10388
|
-
asset: "thumbnail",
|
|
10389
|
-
label: "YouTube thumbnail",
|
|
10390
|
-
kind: "still",
|
|
10391
|
-
ratio: "16:9",
|
|
10392
|
-
px: {
|
|
10393
|
-
w: 1280,
|
|
10394
|
-
h: 720
|
|
10395
|
-
},
|
|
10396
|
-
genre: "card",
|
|
10397
|
-
maxBytes: 2097152,
|
|
10398
|
-
format: "png",
|
|
10399
|
-
accepts: "png|jpg",
|
|
10400
|
-
fit: "cover",
|
|
10401
|
-
text: "expected",
|
|
10402
|
-
safe: {
|
|
10403
|
-
x: 0.05,
|
|
10404
|
-
y: 0.08,
|
|
10405
|
-
w: 0.9,
|
|
10406
|
-
h: 0.84
|
|
10407
|
-
},
|
|
10408
|
-
template: "split-cover",
|
|
10409
|
-
notes: ""
|
|
10410
|
-
},
|
|
10411
|
-
{
|
|
10412
|
-
id: "cws-screenshot",
|
|
10413
|
-
channel: "cws",
|
|
10414
|
-
asset: "screenshot",
|
|
10415
|
-
label: "Chrome Web Store screenshot",
|
|
10416
|
-
kind: "still-set",
|
|
10417
|
-
ratio: "8:5",
|
|
10418
|
-
px: {
|
|
10419
|
-
w: 1280,
|
|
10420
|
-
h: 800
|
|
10421
|
-
},
|
|
10422
|
-
count: {
|
|
10423
|
-
min: 1,
|
|
10424
|
-
max: 5
|
|
10425
|
-
},
|
|
10426
|
-
genre: "screenshot",
|
|
10427
|
-
format: "png",
|
|
10428
|
-
accepts: "png|jpg",
|
|
10429
|
-
fit: "cover",
|
|
10430
|
-
text: "none",
|
|
10431
|
-
safe: {
|
|
10432
|
-
x: 0,
|
|
10433
|
-
y: 0,
|
|
10434
|
-
w: 1,
|
|
10435
|
-
h: 1
|
|
10436
|
-
},
|
|
10437
|
-
notes: "Real UX only \u2014 misleading listing images are a removal-grade violation. Full bleed, square corners."
|
|
10438
|
-
},
|
|
10439
|
-
{
|
|
10440
|
-
id: "cws-small-promo-tile",
|
|
10441
|
-
channel: "cws",
|
|
10442
|
-
asset: "small-promo-tile",
|
|
10443
|
-
label: "Chrome Web Store small promo tile",
|
|
10444
|
-
kind: "still",
|
|
10445
|
-
ratio: "11:7",
|
|
10446
|
-
px: {
|
|
10447
|
-
w: 440,
|
|
10448
|
-
h: 280
|
|
10449
|
-
},
|
|
10450
|
-
genre: "card",
|
|
10451
|
-
format: "png",
|
|
10452
|
-
accepts: "png|jpg",
|
|
10453
|
-
fit: "cover",
|
|
10454
|
-
text: "none",
|
|
10455
|
-
safe: {
|
|
10456
|
-
x: 0,
|
|
10457
|
-
y: 0,
|
|
10458
|
-
w: 1,
|
|
10459
|
-
h: 1
|
|
10460
|
-
},
|
|
10461
|
-
template: "card-on-gradient",
|
|
10462
|
-
notes: "Listings without one rank lower. No text; fill the region; subject centered."
|
|
10463
|
-
},
|
|
10464
|
-
{
|
|
10465
|
-
id: "cws-marquee",
|
|
10466
|
-
channel: "cws",
|
|
10467
|
-
asset: "marquee",
|
|
10468
|
-
label: "Chrome Web Store marquee",
|
|
10469
|
-
kind: "still",
|
|
10470
|
-
ratio: "5:2",
|
|
10471
|
-
px: {
|
|
10472
|
-
w: 1400,
|
|
10473
|
-
h: 560
|
|
10474
|
-
},
|
|
10475
|
-
genre: "card",
|
|
10476
|
-
format: "png",
|
|
10477
|
-
accepts: "png|jpg",
|
|
10478
|
-
fit: "cover",
|
|
10479
|
-
text: "allowed",
|
|
10480
|
-
safe: {
|
|
10481
|
-
x: 0.05,
|
|
10482
|
-
y: 0.08,
|
|
10483
|
-
w: 0.9,
|
|
10484
|
-
h: 0.84
|
|
10485
|
-
},
|
|
10486
|
-
template: "split-cover",
|
|
10487
|
-
notes: "Carousel eligibility."
|
|
10488
|
-
},
|
|
10489
|
-
{
|
|
10490
|
-
id: "cws-icon",
|
|
10491
|
-
channel: "cws",
|
|
10492
|
-
asset: "icon",
|
|
10493
|
-
label: "Chrome Web Store icon",
|
|
10494
|
-
kind: "still",
|
|
10495
|
-
ratio: "1:1",
|
|
10496
|
-
px: {
|
|
10497
|
-
w: 128,
|
|
10498
|
-
h: 128
|
|
10499
|
-
},
|
|
10500
|
-
format: "png",
|
|
10501
|
-
accepts: "png",
|
|
10502
|
-
fit: "cover",
|
|
10503
|
-
text: "none",
|
|
10504
|
-
safe: {
|
|
10505
|
-
x: 0,
|
|
10506
|
-
y: 0,
|
|
10507
|
-
w: 1,
|
|
10508
|
-
h: 1
|
|
10509
|
-
},
|
|
10510
|
-
notes: "96\xD796 art + 16px transparent padding."
|
|
10511
|
-
},
|
|
10512
|
-
{
|
|
10513
|
-
id: "producthunt-thumbnail",
|
|
10514
|
-
channel: "producthunt",
|
|
10515
|
-
asset: "thumbnail",
|
|
10516
|
-
label: "Product Hunt thumbnail",
|
|
10517
|
-
kind: "still",
|
|
10518
|
-
ratio: "1:1",
|
|
10519
|
-
px: {
|
|
10520
|
-
w: 240,
|
|
10521
|
-
h: 240
|
|
10522
|
-
},
|
|
10523
|
-
genre: "card",
|
|
10524
|
-
maxBytes: 3145728,
|
|
10525
|
-
format: "png",
|
|
10526
|
-
accepts: "gif|png",
|
|
10527
|
-
fit: "cover",
|
|
10528
|
-
text: "none",
|
|
10529
|
-
safe: {
|
|
10530
|
-
x: 0.06,
|
|
10531
|
-
y: 0.06,
|
|
10532
|
-
w: 0.88,
|
|
10533
|
-
h: 0.88
|
|
10534
|
-
},
|
|
10535
|
-
template: "card-on-gradient",
|
|
10536
|
-
notes: "GIF animates on hover only \u2014 the first frame must stand alone."
|
|
10537
|
-
},
|
|
10538
|
-
{
|
|
10539
|
-
id: "producthunt-gallery",
|
|
10540
|
-
channel: "producthunt",
|
|
10541
|
-
asset: "gallery",
|
|
10542
|
-
label: "Product Hunt gallery",
|
|
10543
|
-
kind: "still-set",
|
|
10544
|
-
ratio: "127:76",
|
|
10545
|
-
px: {
|
|
10546
|
-
w: 1270,
|
|
10547
|
-
h: 760
|
|
10548
|
-
},
|
|
10549
|
-
count: {
|
|
10550
|
-
min: 4,
|
|
10551
|
-
max: 8
|
|
10552
|
-
},
|
|
10553
|
-
genre: "screenshot",
|
|
10554
|
-
format: "png",
|
|
10555
|
-
accepts: "png|jpg|gif",
|
|
10556
|
-
fit: "cover",
|
|
10557
|
-
text: "none",
|
|
10558
|
-
safe: {
|
|
10559
|
-
x: 0,
|
|
10560
|
-
y: 0,
|
|
10561
|
-
w: 1,
|
|
10562
|
-
h: 1
|
|
10563
|
-
},
|
|
10564
|
-
notes: "First image is the hero."
|
|
10565
|
-
},
|
|
10566
|
-
{
|
|
10567
|
-
id: "x-feed-image",
|
|
10568
|
-
channel: "x",
|
|
10569
|
-
asset: "feed-image",
|
|
10570
|
-
label: "X feed image",
|
|
10571
|
-
kind: "still",
|
|
10572
|
-
ratio: "16:9",
|
|
10573
|
-
px: {
|
|
10574
|
-
w: 1200,
|
|
10575
|
-
h: 675
|
|
10576
|
-
},
|
|
10577
|
-
genre: "card",
|
|
10578
|
-
format: "png",
|
|
10579
|
-
accepts: "png|jpg",
|
|
10580
|
-
fit: "cover",
|
|
10581
|
-
text: "allowed",
|
|
10582
|
-
safe: {
|
|
10583
|
-
x: 0.05,
|
|
10584
|
-
y: 0.08,
|
|
10585
|
-
w: 0.9,
|
|
10586
|
-
h: 0.84
|
|
10587
|
-
},
|
|
10588
|
-
template: "split-cover",
|
|
10589
|
-
notes: ""
|
|
10590
|
-
},
|
|
10591
|
-
{
|
|
10592
|
-
id: "linkedin-feed-image",
|
|
10593
|
-
channel: "linkedin",
|
|
10594
|
-
asset: "feed-image",
|
|
10595
|
-
label: "LinkedIn feed image",
|
|
10596
|
-
kind: "still",
|
|
10597
|
-
ratio: "400:209",
|
|
10598
|
-
px: {
|
|
10599
|
-
w: 1200,
|
|
10600
|
-
h: 627
|
|
10601
|
-
},
|
|
10602
|
-
genre: "card",
|
|
10603
|
-
format: "png",
|
|
10604
|
-
accepts: "png|jpg",
|
|
10605
|
-
fit: "cover",
|
|
10606
|
-
text: "expected",
|
|
10607
|
-
safe: {
|
|
10608
|
-
x: 0.05,
|
|
10609
|
-
y: 0.08,
|
|
10610
|
-
w: 0.9,
|
|
10611
|
-
h: 0.84
|
|
10612
|
-
},
|
|
10613
|
-
template: "split-cover",
|
|
10614
|
-
notes: "1080\xD71350 vertical also performs in the mobile feed."
|
|
10615
|
-
},
|
|
10616
|
-
{
|
|
10617
|
-
id: "og-card",
|
|
10618
|
-
channel: "og",
|
|
10619
|
-
asset: "card",
|
|
10620
|
-
label: "OG card",
|
|
10621
|
-
kind: "still",
|
|
10622
|
-
ratio: "40:21",
|
|
10623
|
-
px: {
|
|
10624
|
-
w: 1200,
|
|
10625
|
-
h: 630
|
|
10626
|
-
},
|
|
10627
|
-
genre: "card",
|
|
10628
|
-
maxBytes: 1048576,
|
|
10629
|
-
format: "png",
|
|
10630
|
-
accepts: "png|jpg",
|
|
10631
|
-
fit: "cover",
|
|
10632
|
-
text: "expected",
|
|
10633
|
-
safe: {
|
|
10634
|
-
x: 0.05,
|
|
10635
|
-
y: 0.0238,
|
|
10636
|
-
w: 0.9,
|
|
10637
|
-
h: 0.9524
|
|
10638
|
-
},
|
|
10639
|
-
template: "split-cover",
|
|
10640
|
-
notes: "Text in the center ~1080\xD7600. Set twitter:card=summary_large_image explicitly \u2014 og:image alone gets the small card."
|
|
10641
|
-
},
|
|
10642
|
-
{
|
|
10643
|
-
id: "github-social-preview",
|
|
10644
|
-
channel: "github",
|
|
10645
|
-
asset: "social-preview",
|
|
10646
|
-
label: "GitHub social preview",
|
|
10647
|
-
kind: "still",
|
|
10648
|
-
ratio: "2:1",
|
|
10649
|
-
px: {
|
|
10650
|
-
w: 1280,
|
|
10651
|
-
h: 640
|
|
10652
|
-
},
|
|
10653
|
-
genre: "card",
|
|
10654
|
-
maxBytes: 1048576,
|
|
10655
|
-
format: "png",
|
|
10656
|
-
accepts: "png|jpg",
|
|
10657
|
-
fit: "cover",
|
|
10658
|
-
text: "expected",
|
|
10659
|
-
safe: {
|
|
10660
|
-
x: 0.0391,
|
|
10661
|
-
y: 0.0781,
|
|
10662
|
-
w: 0.9219,
|
|
10663
|
-
h: 0.8438
|
|
10664
|
-
},
|
|
10665
|
-
template: "card-on-gradient",
|
|
10666
|
-
notes: "Key text \u226550px from every edge."
|
|
10667
10358
|
}
|
|
10668
10359
|
];
|
|
10669
10360
|
function destinationById(id) {
|