@scenar/core 0.9.1 → 0.10.0
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/embed/host-controller.d.ts +7 -0
- package/embed/host-controller.d.ts.map +1 -1
- package/embed/host-controller.js +1 -0
- package/embed/host-controller.js.map +1 -1
- package/embed/protocol.d.ts +35 -3
- package/embed/protocol.d.ts.map +1 -1
- package/embed/protocol.js +37 -4
- package/embed/protocol.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/package.json +1 -1
- package/src/embed/host-controller.test.ts +6 -0
- package/src/embed/host-controller.ts +8 -0
- package/src/embed/protocol.test.ts +53 -0
- package/src/embed/protocol.ts +76 -8
- package/src/index.ts +1 -0
|
@@ -24,6 +24,13 @@ export interface ScenarEmbedHostController {
|
|
|
24
24
|
seek(timeMs: number): void;
|
|
25
25
|
setMuted(muted: boolean): void;
|
|
26
26
|
setVolume(volume: number): void;
|
|
27
|
+
/**
|
|
28
|
+
* Report the scale factor the host is rendering the iframe at (1 = native),
|
|
29
|
+
* so the player's chrome layer can counter-scale its transport controls
|
|
30
|
+
* back to native pixel size. Sent by hosts running the iframe-as-screen
|
|
31
|
+
* mode; players without the capability ignore it.
|
|
32
|
+
*/
|
|
33
|
+
setHostScale(scale: number): void;
|
|
27
34
|
prefetch(): void;
|
|
28
35
|
/**
|
|
29
36
|
* Tell the embed to stop (best effort) and detach the host message listener.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-controller.d.ts","sourceRoot":"","sources":["../../src/embed/host-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD;AAED,2EAA2E;AAC3E,MAAM,WAAW,yBAAyB;IACxC,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,IAAI,IAAI,CAAC;IACjB;;;;OAIG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,sBAA2B,GACnC,yBAAyB,
|
|
1
|
+
{"version":3,"file":"host-controller.d.ts","sourceRoot":"","sources":["../../src/embed/host-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EAGtB,MAAM,eAAe,CAAC;AAEvB;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD;AAED,2EAA2E;AAC3E,MAAM,WAAW,yBAAyB;IACxC,IAAI,IAAI,IAAI,CAAC;IACb,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,IAAI,IAAI,CAAC;IACjB;;;;OAIG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,sBAA2B,GACnC,yBAAyB,CAgC3B"}
|
package/embed/host-controller.js
CHANGED
|
@@ -41,6 +41,7 @@ export function createEmbedHostController(target, options = {}) {
|
|
|
41
41
|
seek: (timeMs) => send({ type: "seek", timeMs }),
|
|
42
42
|
setMuted: (muted) => send({ type: "setMuted", muted }),
|
|
43
43
|
setVolume: (volume) => send({ type: "setVolume", volume }),
|
|
44
|
+
setHostScale: (scale) => send({ type: "setHostScale", scale }),
|
|
44
45
|
prefetch: () => send({ type: "prefetch" }),
|
|
45
46
|
destroy: () => {
|
|
46
47
|
send({ type: "destroy" });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-controller.js","sourceRoot":"","sources":["../../src/embed/host-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,EACjB,eAAe,GAChB,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"host-controller.js","sourceRoot":"","sources":["../../src/embed/host-controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,iBAAiB,EACjB,eAAe,GAChB,MAAM,eAAe,CAAC;AA6CvB;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,yBAAyB,CACvC,MAA6B,EAC7B,UAAkC,EAAE;IAEpC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAClC,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;IAC5B,MAAM,SAAS,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;IAEhD,MAAM,IAAI,GAAG,CAAC,OAA2B,EAAQ,EAAE;QACjD,MAAM,CAAC,aAAa,EAAE,WAAW,CAAC,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IACxE,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,CAAC,KAAmB,EAAQ,EAAE;QAC9C,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,aAAa;YAAE,OAAO;QAClD,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM;YAAE,OAAO;QACpC,MAAM,MAAM,GAAG,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,MAAM;YAAE,OAAO;QACpB,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;IACpB,CAAC,CAAC;IAEF,IAAI,SAAS;QAAE,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAE7D,OAAO;QACL,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAClC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QACpC,IAAI,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAChD,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QACtD,SAAS,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;QAC1D,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;QAC9D,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;QAC1C,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC;YAC1B,IAAI,SAAS;gBAAE,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QAClE,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/embed/protocol.d.ts
CHANGED
|
@@ -18,11 +18,30 @@
|
|
|
18
18
|
export declare const SCENAR_EMBED_SOURCE = "scenar-embed";
|
|
19
19
|
/** Protocol version. Bump only on a breaking change to the message shapes. */
|
|
20
20
|
export declare const SCENAR_EMBED_PROTOCOL_VERSION = 1;
|
|
21
|
+
/**
|
|
22
|
+
* The canonical viewport a packed scenario was authored and bundled at, in CSS
|
|
23
|
+
* pixels — the same numbers `scenar pack` bakes into the bundle and records in
|
|
24
|
+
* `scenario.json`.
|
|
25
|
+
*
|
|
26
|
+
* Carried on `ready` so a host can lay the iframe out at this exact size and
|
|
27
|
+
* scale it as one unit (the iframe-as-screen mode in `@scenar/embed`): the
|
|
28
|
+
* embedded document's media queries then resolve against the canonical width,
|
|
29
|
+
* so responsive components render the same variant they would in a real
|
|
30
|
+
* browser window of that size — instead of the narrow variant the host
|
|
31
|
+
* column's width would select.
|
|
32
|
+
*/
|
|
33
|
+
export interface ScenarEmbedViewport {
|
|
34
|
+
readonly widthPx: number;
|
|
35
|
+
readonly heightPx: number;
|
|
36
|
+
}
|
|
21
37
|
/**
|
|
22
38
|
* Events the embedded player emits to the host (embed -> host).
|
|
23
39
|
*
|
|
24
40
|
* - `ready` — the player has mounted; carries scenario shape the host can use
|
|
25
|
-
* to size or label the frame.
|
|
41
|
+
* to size or label the frame. `viewport` (when the bundle knows it) is the
|
|
42
|
+
* canonical size the scenario was packed at — see {@link ScenarEmbedViewport}.
|
|
43
|
+
* Optional for compatibility: bundles packed before the field existed omit
|
|
44
|
+
* it, and hosts must treat its absence as "fit content inside the iframe".
|
|
26
45
|
* - `resize` — the rendered content height changed (dynamic-height hosts).
|
|
27
46
|
* - `started` / `paused` / `completed` — transport state transitions.
|
|
28
47
|
* - `stepchange` — the active step changed.
|
|
@@ -35,6 +54,7 @@ export type ScenarEmbedEvent = {
|
|
|
35
54
|
readonly type: "ready";
|
|
36
55
|
readonly totalSteps: number;
|
|
37
56
|
readonly hasNarration: boolean;
|
|
57
|
+
readonly viewport?: ScenarEmbedViewport;
|
|
38
58
|
} | {
|
|
39
59
|
readonly type: "resize";
|
|
40
60
|
readonly widthPx: number;
|
|
@@ -63,8 +83,17 @@ export type ScenarEmbedEvent = {
|
|
|
63
83
|
/**
|
|
64
84
|
* Commands the host may send to the embedded player (host -> embed).
|
|
65
85
|
*
|
|
66
|
-
* State-setting commands (`setMuted`, `setVolume`) are
|
|
67
|
-
* a host never has to track the player's internal
|
|
86
|
+
* State-setting commands (`setMuted`, `setVolume`, `setHostScale`) are
|
|
87
|
+
* idempotent by design so a host never has to track the player's internal
|
|
88
|
+
* state to stay in sync.
|
|
89
|
+
*
|
|
90
|
+
* `setHostScale` accompanies the iframe-as-screen mode: when the host lays
|
|
91
|
+
* the iframe out at the canonical viewport and scales it as one unit, every
|
|
92
|
+
* pixel inside — including the player's transport controls — shrinks by that
|
|
93
|
+
* factor. The player's chrome layer counter-scales by `1 / scale` so controls
|
|
94
|
+
* keep rendering at native pixel size (the ViewportChrome contract), which
|
|
95
|
+
* only the host can enable because a cross-origin document cannot observe the
|
|
96
|
+
* transform applied to its own frame.
|
|
68
97
|
*/
|
|
69
98
|
export type ScenarEmbedCommand = {
|
|
70
99
|
readonly type: "play";
|
|
@@ -79,6 +108,9 @@ export type ScenarEmbedCommand = {
|
|
|
79
108
|
} | {
|
|
80
109
|
readonly type: "setVolume";
|
|
81
110
|
readonly volume: number;
|
|
111
|
+
} | {
|
|
112
|
+
readonly type: "setHostScale";
|
|
113
|
+
readonly scale: number;
|
|
82
114
|
} | {
|
|
83
115
|
readonly type: "prefetch";
|
|
84
116
|
} | {
|
package/embed/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/embed/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,2DAA2D;AAC3D,eAAO,MAAM,mBAAmB,iBAAiB,CAAC;AAElD,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B,IAAI,CAAC;AAE/C
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/embed/protocol.ts"],"names":[],"mappings":"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"}
|
package/embed/protocol.js
CHANGED
|
@@ -59,10 +59,33 @@ export function parseEmbedCommand(data) {
|
|
|
59
59
|
return typeof data["muted"] === "boolean" ? { type, muted: data["muted"] } : null;
|
|
60
60
|
case "setVolume":
|
|
61
61
|
return isFiniteNumber(data["volume"]) ? { type, volume: data["volume"] } : null;
|
|
62
|
+
case "setHostScale":
|
|
63
|
+
return isFiniteNumber(data["scale"]) && data["scale"] > 0
|
|
64
|
+
? { type, scale: data["scale"] }
|
|
65
|
+
: null;
|
|
62
66
|
default:
|
|
63
67
|
return null;
|
|
64
68
|
}
|
|
65
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Parse the optional `viewport` field of a `ready` payload. Absent -> the
|
|
72
|
+
* event simply has no viewport (a pre-viewport bundle). Present but malformed
|
|
73
|
+
* -> the whole event is rejected, exactly like a mistyped required field: a
|
|
74
|
+
* half-valid message is a protocol violation, not a degraded mode.
|
|
75
|
+
*/
|
|
76
|
+
function parseReadyViewport(value) {
|
|
77
|
+
if (value === undefined)
|
|
78
|
+
return {};
|
|
79
|
+
if (typeof value !== "object" || value === null)
|
|
80
|
+
return null;
|
|
81
|
+
const record = value;
|
|
82
|
+
return isFiniteNumber(record["widthPx"]) &&
|
|
83
|
+
record["widthPx"] > 0 &&
|
|
84
|
+
isFiniteNumber(record["heightPx"]) &&
|
|
85
|
+
record["heightPx"] > 0
|
|
86
|
+
? { viewport: { widthPx: record["widthPx"], heightPx: record["heightPx"] } }
|
|
87
|
+
: null;
|
|
88
|
+
}
|
|
66
89
|
/**
|
|
67
90
|
* Parse an inbound `MessageEvent.data` into a typed event, or return `null`.
|
|
68
91
|
*
|
|
@@ -79,10 +102,20 @@ export function parseEmbedEvent(data) {
|
|
|
79
102
|
case "completed":
|
|
80
103
|
case "audioBlocked":
|
|
81
104
|
return { type };
|
|
82
|
-
case "ready":
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
105
|
+
case "ready": {
|
|
106
|
+
if (!isFiniteNumber(data["totalSteps"]) || typeof data["hasNarration"] !== "boolean") {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const parsedViewport = parseReadyViewport(data["viewport"]);
|
|
110
|
+
if (parsedViewport === null)
|
|
111
|
+
return null;
|
|
112
|
+
return {
|
|
113
|
+
type,
|
|
114
|
+
totalSteps: data["totalSteps"],
|
|
115
|
+
hasNarration: data["hasNarration"],
|
|
116
|
+
...parsedViewport,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
86
119
|
case "resize":
|
|
87
120
|
return isFiniteNumber(data["widthPx"]) && isFiniteNumber(data["heightPx"])
|
|
88
121
|
? { type, widthPx: data["widthPx"], heightPx: data["heightPx"] }
|
package/embed/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/embed/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,2DAA2D;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAElD,8EAA8E;AAC9E,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../src/embed/protocol.ts"],"names":[],"mappings":"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"}
|
package/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { CLICK_DELAY_MS, TYPE_CHAR_DELAY_MS, HOVER_HOLD_MS, DRAG_SETTLE_MS, CAME
|
|
|
16
16
|
export { findScrollParent, scrollTargetIntoView, scrollTargetIntoViewInstant, } from "./dom/scroll.js";
|
|
17
17
|
export { CURSOR_TARGET_ATTRIBUTE, SCROLL_TARGET_ATTRIBUTE, HOVER_STATE_ATTRIBUTE, DRAG_STATE_ATTRIBUTE, cursorTargetSelector, scrollTargetSelector, } from "./targeting/data-attributes.js";
|
|
18
18
|
export { SCENAR_EMBED_SOURCE, SCENAR_EMBED_PROTOCOL_VERSION, frameEmbedEvent, frameEmbedCommand, parseEmbedCommand, parseEmbedEvent, } from "./embed/protocol.js";
|
|
19
|
-
export type { ScenarEmbedEvent, ScenarEmbedCommand, ScenarEmbedEventMessage, ScenarEmbedCommandMessage, } from "./embed/protocol.js";
|
|
19
|
+
export type { ScenarEmbedEvent, ScenarEmbedCommand, ScenarEmbedEventMessage, ScenarEmbedCommandMessage, ScenarEmbedViewport, } from "./embed/protocol.js";
|
|
20
20
|
export { createEmbedHostController } from "./embed/host-controller.js";
|
|
21
21
|
export type { ScenarEmbedHostController, ScenarEmbedHostTarget, ScenarEmbedHostOptions, } from "./embed/host-controller.js";
|
|
22
22
|
//# sourceMappingURL=index.d.ts.map
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,UAAU,EACV,UAAU,EACV,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,UAAU,EACV,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAGjC,YAAY,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,YAAY,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,UAAU,EACV,UAAU,EACV,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,UAAU,EACV,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAGjC,YAAY,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,YAAY,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,YAAY,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC"}
|
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,sEAAsE;AAgBtE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,UAAU,EACV,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAIrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,mBAAmB;AACnB,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,uBAAuB;AACvB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,oCAAoC;AACpC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAExC,0EAA0E;AAC1E,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,sEAAsE;AAgBtE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,UAAU,EACV,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAIrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,mBAAmB;AACnB,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,uBAAuB;AACvB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,oCAAoC;AACpC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAExC,0EAA0E;AAC1E,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAS7B,uEAAuE;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
|
package/package.json
CHANGED
|
@@ -62,6 +62,12 @@ describe("createEmbedHostController — commands", () => {
|
|
|
62
62
|
EMBED_ORIGIN,
|
|
63
63
|
);
|
|
64
64
|
|
|
65
|
+
controller.setHostScale(0.7);
|
|
66
|
+
expect(post).toHaveBeenLastCalledWith(
|
|
67
|
+
expect.objectContaining({ type: "setHostScale", scale: 0.7 }),
|
|
68
|
+
EMBED_ORIGIN,
|
|
69
|
+
);
|
|
70
|
+
|
|
65
71
|
controller.destroy();
|
|
66
72
|
});
|
|
67
73
|
});
|
|
@@ -32,6 +32,13 @@ export interface ScenarEmbedHostController {
|
|
|
32
32
|
seek(timeMs: number): void;
|
|
33
33
|
setMuted(muted: boolean): void;
|
|
34
34
|
setVolume(volume: number): void;
|
|
35
|
+
/**
|
|
36
|
+
* Report the scale factor the host is rendering the iframe at (1 = native),
|
|
37
|
+
* so the player's chrome layer can counter-scale its transport controls
|
|
38
|
+
* back to native pixel size. Sent by hosts running the iframe-as-screen
|
|
39
|
+
* mode; players without the capability ignore it.
|
|
40
|
+
*/
|
|
41
|
+
setHostScale(scale: number): void;
|
|
35
42
|
prefetch(): void;
|
|
36
43
|
/**
|
|
37
44
|
* Tell the embed to stop (best effort) and detach the host message listener.
|
|
@@ -86,6 +93,7 @@ export function createEmbedHostController(
|
|
|
86
93
|
seek: (timeMs) => send({ type: "seek", timeMs }),
|
|
87
94
|
setMuted: (muted) => send({ type: "setMuted", muted }),
|
|
88
95
|
setVolume: (volume) => send({ type: "setVolume", volume }),
|
|
96
|
+
setHostScale: (scale) => send({ type: "setHostScale", scale }),
|
|
89
97
|
prefetch: () => send({ type: "prefetch" }),
|
|
90
98
|
destroy: () => {
|
|
91
99
|
send({ type: "destroy" });
|
|
@@ -55,6 +55,21 @@ describe("parseEmbedCommand", () => {
|
|
|
55
55
|
});
|
|
56
56
|
});
|
|
57
57
|
|
|
58
|
+
it("accepts setHostScale with a positive finite scale", () => {
|
|
59
|
+
expect(parseEmbedCommand(wrap({ type: "setHostScale", scale: 0.7 }))).toEqual({
|
|
60
|
+
type: "setHostScale",
|
|
61
|
+
scale: 0.7,
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("rejects setHostScale with a missing, non-finite, or non-positive scale", () => {
|
|
66
|
+
expect(parseEmbedCommand(wrap({ type: "setHostScale" }))).toBeNull();
|
|
67
|
+
expect(parseEmbedCommand(wrap({ type: "setHostScale", scale: "big" }))).toBeNull();
|
|
68
|
+
expect(parseEmbedCommand(wrap({ type: "setHostScale", scale: Number.NaN }))).toBeNull();
|
|
69
|
+
expect(parseEmbedCommand(wrap({ type: "setHostScale", scale: 0 }))).toBeNull();
|
|
70
|
+
expect(parseEmbedCommand(wrap({ type: "setHostScale", scale: -1 }))).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
58
73
|
it("rejects a foreign source", () => {
|
|
59
74
|
expect(parseEmbedCommand({ source: "other-widget", v: 1, type: "play" })).toBeNull();
|
|
60
75
|
});
|
|
@@ -103,6 +118,44 @@ describe("parseEmbedEvent", () => {
|
|
|
103
118
|
expect(parseEmbedEvent(wrap({ type: "ready", totalSteps: 3 }))).toBeNull();
|
|
104
119
|
});
|
|
105
120
|
|
|
121
|
+
it("accepts a ready payload carrying a well-formed canonical viewport", () => {
|
|
122
|
+
expect(
|
|
123
|
+
parseEmbedEvent(
|
|
124
|
+
wrap({
|
|
125
|
+
type: "ready",
|
|
126
|
+
totalSteps: 3,
|
|
127
|
+
hasNarration: false,
|
|
128
|
+
viewport: { widthPx: 1440, heightPx: 900 },
|
|
129
|
+
}),
|
|
130
|
+
),
|
|
131
|
+
).toEqual({
|
|
132
|
+
type: "ready",
|
|
133
|
+
totalSteps: 3,
|
|
134
|
+
hasNarration: false,
|
|
135
|
+
viewport: { widthPx: 1440, heightPx: 900 },
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("still accepts a ready payload without a viewport (pre-viewport bundles)", () => {
|
|
140
|
+
const parsed = parseEmbedEvent(wrap({ type: "ready", totalSteps: 3, hasNarration: false }));
|
|
141
|
+
expect(parsed).toEqual({ type: "ready", totalSteps: 3, hasNarration: false });
|
|
142
|
+
// The field must be genuinely absent, not present-and-undefined — hosts
|
|
143
|
+
// branch on `event.viewport` to decide iframe-as-screen adoption.
|
|
144
|
+
expect(parsed && "viewport" in parsed).toBe(false);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("rejects a ready payload whose viewport is malformed", () => {
|
|
148
|
+
const base = { type: "ready", totalSteps: 3, hasNarration: false };
|
|
149
|
+
expect(parseEmbedEvent(wrap({ ...base, viewport: "1440x900" }))).toBeNull();
|
|
150
|
+
expect(parseEmbedEvent(wrap({ ...base, viewport: { widthPx: 1440 } }))).toBeNull();
|
|
151
|
+
expect(
|
|
152
|
+
parseEmbedEvent(wrap({ ...base, viewport: { widthPx: 0, heightPx: 900 } })),
|
|
153
|
+
).toBeNull();
|
|
154
|
+
expect(
|
|
155
|
+
parseEmbedEvent(wrap({ ...base, viewport: { widthPx: "1440", heightPx: 900 } })),
|
|
156
|
+
).toBeNull();
|
|
157
|
+
});
|
|
158
|
+
|
|
106
159
|
it("rejects foreign source and unknown event types", () => {
|
|
107
160
|
expect(parseEmbedEvent({ source: "x", v: 1, type: "started" })).toBeNull();
|
|
108
161
|
expect(parseEmbedEvent(wrap({ type: "imploded" }))).toBeNull();
|
package/src/embed/protocol.ts
CHANGED
|
@@ -21,11 +21,31 @@ export const SCENAR_EMBED_SOURCE = "scenar-embed";
|
|
|
21
21
|
/** Protocol version. Bump only on a breaking change to the message shapes. */
|
|
22
22
|
export const SCENAR_EMBED_PROTOCOL_VERSION = 1;
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* The canonical viewport a packed scenario was authored and bundled at, in CSS
|
|
26
|
+
* pixels — the same numbers `scenar pack` bakes into the bundle and records in
|
|
27
|
+
* `scenario.json`.
|
|
28
|
+
*
|
|
29
|
+
* Carried on `ready` so a host can lay the iframe out at this exact size and
|
|
30
|
+
* scale it as one unit (the iframe-as-screen mode in `@scenar/embed`): the
|
|
31
|
+
* embedded document's media queries then resolve against the canonical width,
|
|
32
|
+
* so responsive components render the same variant they would in a real
|
|
33
|
+
* browser window of that size — instead of the narrow variant the host
|
|
34
|
+
* column's width would select.
|
|
35
|
+
*/
|
|
36
|
+
export interface ScenarEmbedViewport {
|
|
37
|
+
readonly widthPx: number;
|
|
38
|
+
readonly heightPx: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
24
41
|
/**
|
|
25
42
|
* Events the embedded player emits to the host (embed -> host).
|
|
26
43
|
*
|
|
27
44
|
* - `ready` — the player has mounted; carries scenario shape the host can use
|
|
28
|
-
* to size or label the frame.
|
|
45
|
+
* to size or label the frame. `viewport` (when the bundle knows it) is the
|
|
46
|
+
* canonical size the scenario was packed at — see {@link ScenarEmbedViewport}.
|
|
47
|
+
* Optional for compatibility: bundles packed before the field existed omit
|
|
48
|
+
* it, and hosts must treat its absence as "fit content inside the iframe".
|
|
29
49
|
* - `resize` — the rendered content height changed (dynamic-height hosts).
|
|
30
50
|
* - `started` / `paused` / `completed` — transport state transitions.
|
|
31
51
|
* - `stepchange` — the active step changed.
|
|
@@ -35,7 +55,12 @@ export const SCENAR_EMBED_PROTOCOL_VERSION = 1;
|
|
|
35
55
|
* - `error` — an unrecoverable runtime error, with a human-readable message.
|
|
36
56
|
*/
|
|
37
57
|
export type ScenarEmbedEvent =
|
|
38
|
-
| {
|
|
58
|
+
| {
|
|
59
|
+
readonly type: "ready";
|
|
60
|
+
readonly totalSteps: number;
|
|
61
|
+
readonly hasNarration: boolean;
|
|
62
|
+
readonly viewport?: ScenarEmbedViewport;
|
|
63
|
+
}
|
|
39
64
|
| { readonly type: "resize"; readonly widthPx: number; readonly heightPx: number }
|
|
40
65
|
| { readonly type: "started" }
|
|
41
66
|
| { readonly type: "paused" }
|
|
@@ -53,8 +78,17 @@ export type ScenarEmbedEvent =
|
|
|
53
78
|
/**
|
|
54
79
|
* Commands the host may send to the embedded player (host -> embed).
|
|
55
80
|
*
|
|
56
|
-
* State-setting commands (`setMuted`, `setVolume`) are
|
|
57
|
-
* a host never has to track the player's internal
|
|
81
|
+
* State-setting commands (`setMuted`, `setVolume`, `setHostScale`) are
|
|
82
|
+
* idempotent by design so a host never has to track the player's internal
|
|
83
|
+
* state to stay in sync.
|
|
84
|
+
*
|
|
85
|
+
* `setHostScale` accompanies the iframe-as-screen mode: when the host lays
|
|
86
|
+
* the iframe out at the canonical viewport and scales it as one unit, every
|
|
87
|
+
* pixel inside — including the player's transport controls — shrinks by that
|
|
88
|
+
* factor. The player's chrome layer counter-scales by `1 / scale` so controls
|
|
89
|
+
* keep rendering at native pixel size (the ViewportChrome contract), which
|
|
90
|
+
* only the host can enable because a cross-origin document cannot observe the
|
|
91
|
+
* transform applied to its own frame.
|
|
58
92
|
*/
|
|
59
93
|
export type ScenarEmbedCommand =
|
|
60
94
|
| { readonly type: "play" }
|
|
@@ -62,6 +96,7 @@ export type ScenarEmbedCommand =
|
|
|
62
96
|
| { readonly type: "seek"; readonly timeMs: number }
|
|
63
97
|
| { readonly type: "setMuted"; readonly muted: boolean }
|
|
64
98
|
| { readonly type: "setVolume"; readonly volume: number }
|
|
99
|
+
| { readonly type: "setHostScale"; readonly scale: number }
|
|
65
100
|
| { readonly type: "prefetch" }
|
|
66
101
|
| { readonly type: "destroy" };
|
|
67
102
|
|
|
@@ -122,11 +157,35 @@ export function parseEmbedCommand(data: unknown): ScenarEmbedCommand | null {
|
|
|
122
157
|
return typeof data["muted"] === "boolean" ? { type, muted: data["muted"] } : null;
|
|
123
158
|
case "setVolume":
|
|
124
159
|
return isFiniteNumber(data["volume"]) ? { type, volume: data["volume"] } : null;
|
|
160
|
+
case "setHostScale":
|
|
161
|
+
return isFiniteNumber(data["scale"]) && data["scale"] > 0
|
|
162
|
+
? { type, scale: data["scale"] }
|
|
163
|
+
: null;
|
|
125
164
|
default:
|
|
126
165
|
return null;
|
|
127
166
|
}
|
|
128
167
|
}
|
|
129
168
|
|
|
169
|
+
/**
|
|
170
|
+
* Parse the optional `viewport` field of a `ready` payload. Absent -> the
|
|
171
|
+
* event simply has no viewport (a pre-viewport bundle). Present but malformed
|
|
172
|
+
* -> the whole event is rejected, exactly like a mistyped required field: a
|
|
173
|
+
* half-valid message is a protocol violation, not a degraded mode.
|
|
174
|
+
*/
|
|
175
|
+
function parseReadyViewport(
|
|
176
|
+
value: unknown,
|
|
177
|
+
): { viewport?: ScenarEmbedViewport } | null {
|
|
178
|
+
if (value === undefined) return {};
|
|
179
|
+
if (typeof value !== "object" || value === null) return null;
|
|
180
|
+
const record = value as Record<string, unknown>;
|
|
181
|
+
return isFiniteNumber(record["widthPx"]) &&
|
|
182
|
+
record["widthPx"] > 0 &&
|
|
183
|
+
isFiniteNumber(record["heightPx"]) &&
|
|
184
|
+
record["heightPx"] > 0
|
|
185
|
+
? { viewport: { widthPx: record["widthPx"], heightPx: record["heightPx"] } }
|
|
186
|
+
: null;
|
|
187
|
+
}
|
|
188
|
+
|
|
130
189
|
/**
|
|
131
190
|
* Parse an inbound `MessageEvent.data` into a typed event, or return `null`.
|
|
132
191
|
*
|
|
@@ -142,10 +201,19 @@ export function parseEmbedEvent(data: unknown): ScenarEmbedEvent | null {
|
|
|
142
201
|
case "completed":
|
|
143
202
|
case "audioBlocked":
|
|
144
203
|
return { type };
|
|
145
|
-
case "ready":
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
204
|
+
case "ready": {
|
|
205
|
+
if (!isFiniteNumber(data["totalSteps"]) || typeof data["hasNarration"] !== "boolean") {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const parsedViewport = parseReadyViewport(data["viewport"]);
|
|
209
|
+
if (parsedViewport === null) return null;
|
|
210
|
+
return {
|
|
211
|
+
type,
|
|
212
|
+
totalSteps: data["totalSteps"],
|
|
213
|
+
hasNarration: data["hasNarration"],
|
|
214
|
+
...parsedViewport,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
149
217
|
case "resize":
|
|
150
218
|
return isFiniteNumber(data["widthPx"]) && isFiniteNumber(data["heightPx"])
|
|
151
219
|
? { type, widthPx: data["widthPx"], heightPx: data["heightPx"] }
|