@mattebox/player-cast 0.1.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.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +24 -0
  3. package/README.md +54 -0
  4. package/dist/cast-button.d.ts +13 -0
  5. package/dist/cast-button.d.ts.map +1 -0
  6. package/dist/cast-button.js +65 -0
  7. package/dist/cast-button.js.map +1 -0
  8. package/dist/cast-screen.d.ts +24 -0
  9. package/dist/cast-screen.d.ts.map +1 -0
  10. package/dist/cast-screen.js +148 -0
  11. package/dist/cast-screen.js.map +1 -0
  12. package/dist/cast.d.ts +77 -0
  13. package/dist/cast.d.ts.map +1 -0
  14. package/dist/cast.js +343 -0
  15. package/dist/cast.js.map +1 -0
  16. package/dist/cdn/mattebox-player-cast.min.js +91 -0
  17. package/dist/component.d.ts +48 -0
  18. package/dist/component.d.ts.map +1 -0
  19. package/dist/component.js +89 -0
  20. package/dist/component.js.map +1 -0
  21. package/dist/es2015/cast-button.js +59 -0
  22. package/dist/es2015/cast-screen.js +151 -0
  23. package/dist/es2015/cast.js +299 -0
  24. package/dist/es2015/component.js +84 -0
  25. package/dist/es2015/host.js +19 -0
  26. package/dist/es2015/icons.js +23 -0
  27. package/dist/es2015/index.js +17 -0
  28. package/dist/es2015/shared.js +125 -0
  29. package/dist/es2015/slider.js +154 -0
  30. package/dist/host.d.ts +14 -0
  31. package/dist/host.d.ts.map +1 -0
  32. package/dist/host.js +19 -0
  33. package/dist/host.js.map +1 -0
  34. package/dist/icons.d.ts +33 -0
  35. package/dist/icons.d.ts.map +1 -0
  36. package/dist/icons.js +52 -0
  37. package/dist/icons.js.map +1 -0
  38. package/dist/index.d.ts +22 -0
  39. package/dist/index.d.ts.map +1 -0
  40. package/dist/index.js +21 -0
  41. package/dist/index.js.map +1 -0
  42. package/dist/shared.d.ts +29 -0
  43. package/dist/shared.d.ts.map +1 -0
  44. package/dist/shared.js +125 -0
  45. package/dist/shared.js.map +1 -0
  46. package/dist/slider.d.ts +28 -0
  47. package/dist/slider.d.ts.map +1 -0
  48. package/dist/slider.js +165 -0
  49. package/dist/slider.js.map +1 -0
  50. package/package.json +59 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Josep Boix Requesens
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/NOTICE ADDED
@@ -0,0 +1,24 @@
1
+ @mattebox/player-cast carries two glyphs copied from media-icons
2
+ (https://github.com/vidstack/media-icons), used under the MIT License:
3
+
4
+ MIT License
5
+
6
+ Copyright (c) 2023 Rahim Alwer
7
+
8
+ Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ of this software and associated documentation files (the "Software"), to deal
10
+ in the Software without restriction, including without limitation the rights
11
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ copies of the Software, and to permit persons to whom the Software is
13
+ furnished to do so, subject to the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be included in all
16
+ copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,54 @@
1
+ <h1>
2
+ <img src="https://raw.githubusercontent.com/jboix/mattebox-player/main/docs/logo.svg" width="44" height="44" align="absmiddle" alt="">
3
+ @mattebox/player-cast
4
+ </h1>
5
+
6
+ `<mbx-cast-button>` and `<mbx-cast-screen>` for the
7
+ [Mattebox player](https://github.com/jboix/mattebox-player): Chromecast
8
+ from the page, over Google's sender SDK. The button starts and ends a
9
+ session, and the screen covers the picture while the receiver plays, with
10
+ its name, play and pause, the seek slider, the time and a stop button.
11
+ Importing the package registers both elements.
12
+
13
+ It is a package of its own because the button loads Google's script onto
14
+ the page, which the player never does on an integrator's behalf. The AirPlay
15
+ button, over Safari's own API, ships with the player.
16
+
17
+ ```sh
18
+ npm install @mattebox/player-cast @mattebox/player mattebox
19
+ ```
20
+
21
+ ```html
22
+ <mattebox-player src="https://example.com/vod/master.m3u8" controls="custom">
23
+ <mbx-cast-screen></mbx-cast-screen>
24
+ <mbx-control-bar>
25
+ <mbx-play-button></mbx-play-button>
26
+ <mbx-seek-bar></mbx-seek-bar>
27
+ <mbx-spacer></mbx-spacer>
28
+ <mbx-cast-button></mbx-cast-button>
29
+ </mbx-control-bar>
30
+ </mattebox-player>
31
+
32
+ <script type="module">
33
+ import '@mattebox/player';
34
+ import '@mattebox/player-cast';
35
+ </script>
36
+ ```
37
+
38
+ On start the player pauses the video, freezes the engine so nothing is
39
+ fetched for a paused picture, and sends the session's source to the
40
+ receiver at the video's time. On end the engine resumes and the video
41
+ picks up where the receiver stopped. The receiver plays the URL by itself,
42
+ so DRM and signed URLs are the page's to arrange through the `castload`
43
+ event, which carries the load request before it goes out:
44
+
45
+ ```ts
46
+ button.addEventListener('castload', (event) => {
47
+ event.detail.customData = { licenseUrl: 'https://drm.example/widevine' };
48
+ });
49
+ ```
50
+
51
+ `receiver` on the button names the receiver application, the Default Media
52
+ Receiver without one; `sdk="none"` leaves loading the SDK to the page. The
53
+ [guide](https://github.com/jboix/mattebox-player/blob/main/docs/guide/03-the-element.md#casting)
54
+ covers the rest.
@@ -0,0 +1,13 @@
1
+ import { Component } from './component.js';
2
+ import type { PlayerHost } from './host.js';
3
+ export declare class MbxCastButton extends Component {
4
+ static get observedAttributes(): readonly string[];
5
+ private readonly button;
6
+ private readonly slots;
7
+ private api;
8
+ constructor();
9
+ protected attach(player: PlayerHost): void;
10
+ protected detach(): void;
11
+ protected render(): void;
12
+ }
13
+ //# sourceMappingURL=cast-button.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast-button.d.ts","sourceRoot":"","sources":["../src/cast-button.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAO5C,qBAAa,aAAc,SAAQ,SAAS;IAC1C,MAAM,KAAK,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAEjD;IAED,iBAAyB,MAAM,CAAoB;IACnD,iBAAyB,KAAK,CAAiC;IAC/D,QAAgB,GAAG,CAAc;;cAoBd,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;cAgBhC,MAAM,IAAI,IAAI;cAKd,MAAM,IAAI,IAAI;CAkBlC"}
@@ -0,0 +1,65 @@
1
+ import { cast } from './cast.js';
2
+ import { Component } from './component.js';
3
+ import { icon } from './icons.js';
4
+ import { BUTTON_STYLE, iconSlots, show, style } from './shared.js';
5
+ const STATES = ['default', 'active'];
6
+ export class MbxCastButton extends Component {
7
+ static get observedAttributes() {
8
+ return ['label-start', 'label-stop'];
9
+ }
10
+ constructor() {
11
+ super();
12
+ this.api = null;
13
+ const root = this.attachShadow({ mode: 'open' });
14
+ this.button = document.createElement('button');
15
+ this.button.type = 'button';
16
+ this.button.setAttribute('part', 'button');
17
+ this.slots = iconSlots(STATES, (state) => icon(state === 'active' ? 'cast-active' : 'cast'));
18
+ for (const state of STATES)
19
+ this.button.append(this.slots[state]);
20
+ this.button.addEventListener('click', () => {
21
+ const api = this.api;
22
+ if (api === null)
23
+ return;
24
+ if (api.state() === 'casting')
25
+ api.stop();
26
+ else
27
+ api.start(this);
28
+ });
29
+ root.append(style(BUTTON_STYLE), this.button);
30
+ }
31
+ attach(player) {
32
+ const api = cast(player);
33
+ this.api = api;
34
+ api.connect(this.getAttribute('receiver'), this.getAttribute('sdk'));
35
+ this.keep(api.watch(() => {
36
+ this.render();
37
+ }));
38
+ // A session decides whether there is anything to send.
39
+ this.follow(player, () => {
40
+ this.render();
41
+ return undefined;
42
+ });
43
+ }
44
+ detach() {
45
+ this.api?.disconnect();
46
+ this.api = null;
47
+ }
48
+ render() {
49
+ const player = this.player;
50
+ const api = this.api;
51
+ if (player === null || api === null)
52
+ return;
53
+ const state = api.state();
54
+ const active = state === 'casting';
55
+ this.hidden = state === 'unavailable';
56
+ // Nothing loaded means nothing to send; the receiver would show its idle screen.
57
+ this.button.disabled = !active && player.player?.session == null;
58
+ this.button.toggleAttribute('aria-busy', state === 'connecting');
59
+ show(this.slots, active ? 'active' : 'default');
60
+ this.button.setAttribute('aria-label', active
61
+ ? (this.getAttribute('label-stop') ?? 'Stop casting')
62
+ : (this.getAttribute('label-start') ?? 'Cast'));
63
+ }
64
+ }
65
+ //# sourceMappingURL=cast-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast-button.js","sourceRoot":"","sources":["../src/cast-button.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Component } from './component.js';
2
+ import type { PlayerHost } from './host.js';
3
+ export declare class MbxCastScreen extends Component {
4
+ static get observedAttributes(): readonly string[];
5
+ private readonly title_;
6
+ private readonly play;
7
+ private readonly slots;
8
+ private readonly seek;
9
+ private readonly row;
10
+ private readonly time;
11
+ private readonly stop;
12
+ private api;
13
+ /** Where the thumb is while a pointer holds it, so the receiver's reports do not move it back. */
14
+ private held;
15
+ constructor();
16
+ /** Hidden before attaching: a constructor must not add attributes. */
17
+ connectedCallback(): void;
18
+ protected attach(player: PlayerHost): void;
19
+ protected detach(): void;
20
+ /** The slider and the time, from the receiver or from the held thumb. */
21
+ private paint;
22
+ protected render(): void;
23
+ }
24
+ //# sourceMappingURL=cast-screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast-screen.d.ts","sourceRoot":"","sources":["../src/cast-screen.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAwD5C,qBAAa,aAAc,SAAQ,SAAS;IAC1C,MAAM,KAAK,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAEjD;IAED,iBAAyB,MAAM,CAAc;IAC7C,iBAAyB,IAAI,CAAoB;IACjD,iBAAyB,KAAK,CAAiC;IAC/D,iBAAyB,IAAI,CAAS;IACtC,iBAAyB,GAAG,CAAc;IAC1C,iBAAyB,IAAI,CAAc;IAC3C,iBAAyB,IAAI,CAAoB;IACjD,QAAgB,GAAG,CAAc;IACjC,kGAAkG;IAClG,QAAgB,IAAI,CAAgB;;IA8CpC,sEAAsE;IAC7D,iBAAiB,IAAI,IAAI;cAKf,MAAM,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;cAWhC,MAAM,IAAI,IAAI;IAKjC,yEAAyE;IACzE,OAAO,CAAC,KAAK;cAcM,MAAM,IAAI,IAAI;CAsBlC"}
@@ -0,0 +1,148 @@
1
+ import { cast } from './cast.js';
2
+ import { Component } from './component.js';
3
+ import { icon } from './icons.js';
4
+ import { BUTTON_STYLE, describe, el, fill, format, iconSlots, SLIDER_STYLE, show, style, } from './shared.js';
5
+ import { slider } from './slider.js';
6
+ const STATES = ['play', 'pause'];
7
+ const STEP = 5;
8
+ const PAGE = 30;
9
+ const STYLE = `${BUTTON_STYLE}
10
+ ${SLIDER_STYLE}
11
+ :host {
12
+ position: absolute;
13
+ inset: 0;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ padding: 16px;
18
+ background: rgba(0, 0, 0, 0.85);
19
+ color: var(--mbx-text);
20
+ font: 400 15px/1.4 var(--mbx-font);
21
+ text-align: center;
22
+ }
23
+ :host([hidden]) { display: none; }
24
+ [part~="box"] { display: flex; flex-direction: column; align-items: center; gap: 12px; width: min(100%, 420px); }
25
+ [part~="title"] { font-size: 18px; font-weight: 600; }
26
+ [part~="row"] { display: flex; align-items: center; justify-content: center; gap: 12px; width: 100%; }
27
+ [part~="slider"] { flex: 1; }
28
+ [part~="time"] { font-variant-numeric: tabular-nums; color: var(--mbx-muted); font-size: 13px; }
29
+ [part~="stop"] {
30
+ height: 36px;
31
+ padding: 0 16px;
32
+ font: inherit;
33
+ font-weight: 600;
34
+ color: inherit;
35
+ background: transparent;
36
+ border: 1px solid currentColor;
37
+ border-radius: var(--mbx-radius);
38
+ cursor: pointer;
39
+ }
40
+ [part~="stop"]:focus-visible { outline: 2px solid var(--mbx-accent); outline-offset: -2px; }
41
+ [hidden] { display: none; }
42
+ `;
43
+ export class MbxCastScreen extends Component {
44
+ static get observedAttributes() {
45
+ return ['label', 'label-stop', 'label-play', 'label-pause', 'label-seek'];
46
+ }
47
+ constructor() {
48
+ super();
49
+ this.api = null;
50
+ this.held = null;
51
+ const root = this.attachShadow({ mode: 'open' });
52
+ const box = el('div', 'box');
53
+ this.title_ = el('div', 'title');
54
+ this.play = document.createElement('button');
55
+ this.play.type = 'button';
56
+ this.play.setAttribute('part', 'button play');
57
+ this.slots = iconSlots(STATES, icon);
58
+ for (const state of STATES)
59
+ this.play.append(this.slots[state]);
60
+ this.play.addEventListener('click', () => {
61
+ this.api?.playPause();
62
+ });
63
+ this.seek = slider({
64
+ step: () => STEP,
65
+ page: () => PAGE,
66
+ onInput: (value) => {
67
+ if (this.seek.dragging()) {
68
+ this.held = value;
69
+ this.paint();
70
+ return;
71
+ }
72
+ this.held = null;
73
+ this.api?.seek(value);
74
+ },
75
+ onDrag: (on) => {
76
+ this.toggleAttribute('dragging', on);
77
+ if (!on)
78
+ this.held = null;
79
+ },
80
+ });
81
+ this.time = el('span', 'time');
82
+ this.row = el('div', 'row');
83
+ this.row.append(this.play, this.seek.root, this.time);
84
+ this.stop = el('button', 'stop');
85
+ this.stop.type = 'button';
86
+ this.stop.addEventListener('click', () => {
87
+ this.api?.stop();
88
+ });
89
+ box.append(this.title_, this.row, this.stop);
90
+ root.append(style(STYLE), box);
91
+ }
92
+ /** Hidden before attaching: a constructor must not add attributes. */
93
+ connectedCallback() {
94
+ if (this.player === null)
95
+ this.hidden = true;
96
+ super.connectedCallback();
97
+ }
98
+ attach(player) {
99
+ const api = cast(player);
100
+ this.api = api;
101
+ this.keep(api.watch(() => {
102
+ this.render();
103
+ }));
104
+ this.render();
105
+ }
106
+ detach() {
107
+ this.api = null;
108
+ this.hidden = true;
109
+ }
110
+ /** The slider and the time, from the receiver or from the held thumb. */
111
+ paint() {
112
+ const api = this.api;
113
+ if (api === null)
114
+ return;
115
+ const remote = api.remote();
116
+ const seekable = remote.loaded && Number.isFinite(remote.duration);
117
+ this.seek.root.hidden = !seekable;
118
+ this.time.hidden = !seekable;
119
+ if (!seekable)
120
+ return;
121
+ const at = this.held ?? remote.time;
122
+ this.seek.range(0, remote.duration);
123
+ this.seek.set(at, describe(at, remote.duration));
124
+ this.time.textContent = `${format(at)} / ${format(remote.duration)}`;
125
+ }
126
+ render() {
127
+ const api = this.api;
128
+ if (api === null)
129
+ return;
130
+ const remote = api.remote();
131
+ // Named while hidden too: a screen reader walks the tree either way.
132
+ const state = remote.loaded && !remote.paused ? 'pause' : 'play';
133
+ show(this.slots, state);
134
+ this.play.setAttribute('aria-label', this.getAttribute(`label-${state}`) ?? (state === 'play' ? 'Play' : 'Pause'));
135
+ this.seek.label(this.getAttribute('label-seek') ?? 'Seek');
136
+ this.stop.textContent = this.getAttribute('label-stop') ?? 'Stop casting';
137
+ const casting = api.state() === 'casting';
138
+ this.hidden = !casting;
139
+ if (!casting)
140
+ return;
141
+ this.title_.textContent = fill(this.getAttribute('label') ?? 'Casting to {device}', {
142
+ device: api.device() ?? '',
143
+ });
144
+ this.play.disabled = !remote.loaded;
145
+ this.paint();
146
+ }
147
+ }
148
+ //# sourceMappingURL=cast-screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast-screen.js","sourceRoot":"","sources":["../src/cast-screen.ts"],"names":[],"mappings":"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"}
package/dist/cast.d.ts ADDED
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Chromecast from the page, over Google's Cast sender framework. The SDK is
3
+ * a script the page loads, never a dependency (rule 2): the helper puts it
4
+ * on the page the first time a cast button connects, unless it is already
5
+ * there or the button says `sdk="none"`. Its context is one per page by
6
+ * Google's design, so the receiver id the first button gives is the one
7
+ * the page keeps.
8
+ *
9
+ * One `Cast` per player. It owns the handoff and nothing on the video is
10
+ * wrapped (rule 3): on start it pauses the video, freezes the engine, and
11
+ * sends the session's source to the receiver at the video's time; on end
12
+ * it thaws the engine, seeks the video to the receiver's last time, and
13
+ * plays if the receiver was playing. The cast screen drives the receiver
14
+ * through the SDK's remote player meanwhile.
15
+ *
16
+ * The receiver plays the URL by itself, so DRM and signed URLs are the
17
+ * page's to arrange: the button dispatches `castload` with the request
18
+ * before it goes out, and the page sets `customData` or cancels.
19
+ */
20
+ import type { PlayerHost } from './host.js';
21
+ /** What the receiver plays. The page sees it as `castload`'s `detail`. */
22
+ export interface MediaInfo {
23
+ contentId: string;
24
+ contentType: string;
25
+ streamType: string;
26
+ tracks: Track[] | null;
27
+ hlsSegmentFormat?: string;
28
+ hlsVideoSegmentFormat?: string;
29
+ }
30
+ export interface Track {
31
+ trackContentId: string;
32
+ trackContentType: string;
33
+ subtype: string;
34
+ name: string;
35
+ language: string;
36
+ }
37
+ export interface LoadRequest {
38
+ media: MediaInfo;
39
+ currentTime: number;
40
+ autoplay: boolean;
41
+ customData: unknown;
42
+ }
43
+ export type CastState = 'unavailable' | 'idle' | 'connecting' | 'casting';
44
+ /** What the cast screen shows of the receiver. */
45
+ export interface Remote {
46
+ readonly loaded: boolean;
47
+ readonly paused: boolean;
48
+ readonly time: number;
49
+ /** Infinity for a live stream, the way the video reports it. */
50
+ readonly duration: number;
51
+ }
52
+ export interface Cast {
53
+ /**
54
+ * Puts the SDK on the page unless `sdk` is `none` or it is already there,
55
+ * and sets the context up with `receiver`, or the default receiver. The
56
+ * first call on the page decides both; a later different receiver is an
57
+ * error on the console, not a second context.
58
+ */
59
+ connect(receiver: string | null, sdk: string | null): void;
60
+ /** The button left the player: a rejoined session no longer looks here. */
61
+ disconnect(): void;
62
+ state(): CastState;
63
+ /** The receiver's name while casting, or null. */
64
+ device(): string | null;
65
+ remote(): Remote;
66
+ /** Opens the device picker. `castload` goes out on `target` once a session starts. */
67
+ start(target: HTMLElement): void;
68
+ /** Ends the session and stops the receiver. */
69
+ stop(): void;
70
+ playPause(): void;
71
+ seek(time: number): void;
72
+ /** Runs `fn` on every change: devices, session, and the receiver's playback. Returns the unsubscribe. */
73
+ watch(fn: () => void): () => void;
74
+ }
75
+ /** The player's Cast, one per player, shared by its cast button and cast screen. */
76
+ export declare function cast(player: PlayerHost): Cast;
77
+ //# sourceMappingURL=cast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cast.d.ts","sourceRoot":"","sources":["../src/cast.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAwD5C,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,WAAW,KAAK;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;CACrB;AAuCD,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,CAAC;AAE1E,kDAAkD;AAClD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,IAAI;IACnB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3D,2EAA2E;IAC3E,UAAU,IAAI,IAAI,CAAC;IACnB,KAAK,IAAI,SAAS,CAAC;IACnB,kDAAkD;IAClD,MAAM,IAAI,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,IAAI,MAAM,CAAC;IACjB,sFAAsF;IACtF,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IACjC,+CAA+C;IAC/C,IAAI,IAAI,IAAI,CAAC;IACb,SAAS,IAAI,IAAI,CAAC;IAClB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yGAAyG;IACzG,KAAK,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CACnC;AAuVD,oFAAoF;AACpF,wBAAgB,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,CAO7C"}