@wyndlass/app-sdk 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +196 -2
- package/cli/app-package.mjs +111 -0
- package/cli/commands.mjs +114 -0
- package/cli/main.mjs +66 -0
- package/cli/package.mjs +119 -0
- package/cli/preview.mjs +142 -0
- package/cli/zip.mjs +63 -0
- package/dist/contract/app-contract.d.ts +221 -0
- package/dist/contract/app-contract.js +6 -0
- package/dist/contract/app-definition.d.ts +155 -0
- package/dist/contract/app-definition.js +3 -0
- package/dist/contract/data-answer.d.ts +40 -0
- package/dist/contract/data-answer.js +3 -0
- package/dist/contract/data-calendar.d.ts +57 -0
- package/dist/contract/data-calendar.js +3 -0
- package/dist/contract/data-news.d.ts +29 -0
- package/dist/contract/data-news.js +3 -0
- package/dist/contract/data-quotes.d.ts +75 -0
- package/dist/contract/data-quotes.js +3 -0
- package/dist/contract/data-series.d.ts +51 -0
- package/dist/contract/data-series.js +3 -0
- package/dist/contract/data-snapshot.d.ts +30 -0
- package/dist/contract/data-snapshot.js +3 -0
- package/dist/contract/data-weather.d.ts +421 -0
- package/dist/contract/data-weather.js +3 -0
- package/dist/contract/player-api-event.d.ts +253 -0
- package/dist/contract/player-api-event.js +3 -0
- package/dist/contract/player-api-status.d.ts +178 -0
- package/dist/contract/player-api-status.js +3 -0
- package/dist/easing.d.ts +16 -0
- package/dist/easing.js +71 -0
- package/dist/index.d.ts +94 -0
- package/dist/index.js +218 -0
- package/dist/preview/frame/frame.js +2 -0
- package/dist/preview/frame/index.html +12 -0
- package/dist/preview/index.html +40 -0
- package/dist/preview/page.js +7 -0
- package/dist/qr.d.ts +5 -0
- package/dist/qr.js +14 -0
- package/dist/react.d.ts +52 -0
- package/dist/react.js +149 -0
- package/package.json +83 -3
- package/protocol/app-definition.schema.json +435 -0
- package/protocol/app-packages.json +172 -0
- package/protocol/common.schema.json +197 -0
- package/protocol/samples/calendar.json +63 -0
- package/protocol/samples/events/app.command.json +14 -0
- package/protocol/samples/events/item.end.json +13 -0
- package/protocol/samples/events/item.start.json +18 -0
- package/protocol/samples/events/state.changed.json +11 -0
- package/protocol/samples/news.json +37 -0
- package/protocol/samples/player-status.json +58 -0
- package/protocol/samples/weather.json +59 -0
- package/templates/react/AGENTS.md +13 -0
- package/templates/react/README.md +22 -0
- package/templates/react/app.json +14 -0
- package/templates/react/gitignore +2 -0
- package/templates/react/package.json +22 -0
- package/templates/react/src/app.css +7 -0
- package/templates/react/src/app.tsx +39 -0
- package/templates/react/src/env.d.ts +5 -0
- package/templates/react/tsconfig.json +13 -0
- package/templates/vanilla/AGENTS.md +13 -0
- package/templates/vanilla/README.md +22 -0
- package/templates/vanilla/app.json +14 -0
- package/templates/vanilla/gitignore +2 -0
- package/templates/vanilla/package.json +18 -0
- package/templates/vanilla/src/app.css +7 -0
- package/templates/vanilla/src/app.ts +59 -0
- package/templates/vanilla/src/env.d.ts +5 -0
- package/templates/vanilla/tsconfig.json +12 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
12
|
+
var _WyndlassApp_instances, _WyndlassApp_said, _WyndlassApp_hooks, _WyndlassApp_mounted, _WyndlassApp_observer, _WyndlassApp_lastItems, _WyndlassApp_sized, _WyndlassApp_present;
|
|
13
|
+
/**
|
|
14
|
+
* The app runtime (WP-0101, ADR 0101): what every app on Wyndlass's screens shares, Wyndlass's own and an
|
|
15
|
+
* organization's. Its context from the host, its props with their defaults, drawing from the layout's clock (so seeking
|
|
16
|
+
* and pausing draw the right moment), saying it is ready once, and its enter and exit presets on that clock.
|
|
17
|
+
*
|
|
18
|
+
* Both shapes of the app contract (WP-0100, ADR 0100): a host of Player API 2 defines the bundle's default export under
|
|
19
|
+
* a tag of its own and hands it the context once, through ATTACH, leaving nothing on the element; an older one reads the
|
|
20
|
+
* tag the bundle defined (`define`) and sets the context as `element.wyndlass`.
|
|
21
|
+
*
|
|
22
|
+
* Outside a host (a unit test, the SDK's preview before it gives one) an app still runs: the time is now, each prop is
|
|
23
|
+
* its default, data and images are refused as unavailable, and ready() does nothing.
|
|
24
|
+
*/
|
|
25
|
+
import { ATTACH } from "./contract/app-contract.js";
|
|
26
|
+
import { cubicBezier, easingOf, staggerTimes } from "./easing.js";
|
|
27
|
+
export { ATTACH };
|
|
28
|
+
export { cubicBezier, easingOf, staggerTimes } from "./easing.js";
|
|
29
|
+
/** The contexts handed over through ATTACH, where no other script on the page can reach them. */
|
|
30
|
+
const given = new WeakMap();
|
|
31
|
+
// What acts or reads for an app is a function of this module, never a method or getter of the element: another app on
|
|
32
|
+
// the page can reach the element, but not this module's scope, so it cannot use the app's context through it.
|
|
33
|
+
/** An app's context, however the host gave it; none outside a host. */
|
|
34
|
+
export function contextOf(app) { return given.get(app) ?? app.wyndlass; }
|
|
35
|
+
const unavailable = () => new Error("unavailable: no host gives this app data here");
|
|
36
|
+
/** The data service's answer for a source the definition declares (data-answer.schema.json); refused outside a host. */
|
|
37
|
+
export function data(app, source, params) {
|
|
38
|
+
const c = contextOf(app);
|
|
39
|
+
return c ? c.data(source, params) : Promise.reject(unavailable());
|
|
40
|
+
}
|
|
41
|
+
/** The answer the screen last kept for the same request, however old, at once (WP-0111); none before the first, or here. */
|
|
42
|
+
export function kept(app, source, params) {
|
|
43
|
+
return contextOf(app)?.kept?.(source, params);
|
|
44
|
+
}
|
|
45
|
+
/** An image a data answer names, as a blob URL the app can draw; refused outside a host. */
|
|
46
|
+
export function image(app, path) {
|
|
47
|
+
const c = contextOf(app);
|
|
48
|
+
return c ? c.image(path) : Promise.reject(unavailable());
|
|
49
|
+
}
|
|
50
|
+
/** A data table's snapshot as the manifest carries it, the bound table's with only the rows the binding chooses; null without one. */
|
|
51
|
+
export function snapshot(app, table) {
|
|
52
|
+
return contextOf(app)?.snapshot(table) ?? null;
|
|
53
|
+
}
|
|
54
|
+
/** The app's view of the player's controller (ADR 0050), on screens, as its definition's `capabilities.player` allows. */
|
|
55
|
+
export function controllerOf(app) { return contextOf(app)?.controller; }
|
|
56
|
+
/** The people at the screen (WP-0105), where the definition declares the impressions sensor and the site measures. */
|
|
57
|
+
export function impressionsOf(app) { return contextOf(app)?.impressions; }
|
|
58
|
+
export class WyndlassApp extends HTMLElement {
|
|
59
|
+
constructor() {
|
|
60
|
+
super(...arguments);
|
|
61
|
+
_WyndlassApp_instances.add(this);
|
|
62
|
+
_WyndlassApp_said.set(this, false);
|
|
63
|
+
_WyndlassApp_hooks.set(this, []);
|
|
64
|
+
_WyndlassApp_mounted.set(this, false);
|
|
65
|
+
_WyndlassApp_observer.set(this, void 0);
|
|
66
|
+
_WyndlassApp_lastItems.set(this, []);
|
|
67
|
+
}
|
|
68
|
+
/** Player API 2: the host hands the context over once, before the element joins the document; a second is ignored. */
|
|
69
|
+
[(_WyndlassApp_said = new WeakMap(), _WyndlassApp_hooks = new WeakMap(), _WyndlassApp_mounted = new WeakMap(), _WyndlassApp_observer = new WeakMap(), _WyndlassApp_lastItems = new WeakMap(), _WyndlassApp_instances = new WeakSet(), ATTACH)](context) { if (!given.has(this))
|
|
70
|
+
given.set(this, context); }
|
|
71
|
+
connectedCallback() {
|
|
72
|
+
// Its box's size as --w and --h, for text sized to it: container queries need Chromium 105, and the floor is 94 (ADR 0051).
|
|
73
|
+
__classPrivateFieldGet(this, _WyndlassApp_instances, "m", _WyndlassApp_sized).call(this);
|
|
74
|
+
if (typeof ResizeObserver === "function" && !__classPrivateFieldGet(this, _WyndlassApp_observer, "f")) {
|
|
75
|
+
__classPrivateFieldSet(this, _WyndlassApp_observer, new ResizeObserver(() => __classPrivateFieldGet(this, _WyndlassApp_instances, "m", _WyndlassApp_sized).call(this)), "f");
|
|
76
|
+
__classPrivateFieldGet(this, _WyndlassApp_observer, "f").observe(this);
|
|
77
|
+
}
|
|
78
|
+
if (__classPrivateFieldGet(this, _WyndlassApp_mounted, "f"))
|
|
79
|
+
return;
|
|
80
|
+
__classPrivateFieldSet(this, _WyndlassApp_mounted, true, "f");
|
|
81
|
+
this.root = this.shadowRoot ?? this.attachShadow({ mode: "open" });
|
|
82
|
+
// Every app's background: its own prop, transparent by default, so the layout shows through.
|
|
83
|
+
this.style.background = color(this.prop("background", "#00000000"), "#00000000");
|
|
84
|
+
this.mount();
|
|
85
|
+
this.update(this.now());
|
|
86
|
+
}
|
|
87
|
+
disconnectedCallback() { __classPrivateFieldGet(this, _WyndlassApp_observer, "f")?.disconnect(); __classPrivateFieldSet(this, _WyndlassApp_observer, undefined, "f"); }
|
|
88
|
+
/** A prop, or its default when it is absent or empty. */
|
|
89
|
+
prop(name, fallback) {
|
|
90
|
+
const v = contextOf(this)?.props?.[name];
|
|
91
|
+
return (v === undefined || v === null || v === "" ? fallback : v);
|
|
92
|
+
}
|
|
93
|
+
/** The font prop, a typography value (layout.schema.json#/$defs/typography, WP-0068 part e), as CSS for the app's box. */
|
|
94
|
+
font() {
|
|
95
|
+
return typographyCss(this.prop("font", null));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Styles for the app's shadow root, as a constructable stylesheet (adoptedStyleSheets), not a <style>: a page whose
|
|
99
|
+
* style policy uses nonces still draws it (WP-0124 decision 9). Each call adds a sheet after the others. Call it from
|
|
100
|
+
* mount(), once the root is there.
|
|
101
|
+
*/
|
|
102
|
+
css(text) {
|
|
103
|
+
const sheet = new CSSStyleSheet();
|
|
104
|
+
sheet.replaceSync(text);
|
|
105
|
+
this.root.adoptedStyleSheets = [...this.root.adoptedStyleSheets, sheet];
|
|
106
|
+
return sheet;
|
|
107
|
+
}
|
|
108
|
+
/** The layout's clock: now, or the moment it is held at when seeked or paused. */
|
|
109
|
+
now() { return contextOf(this)?.now() ?? Date.now(); }
|
|
110
|
+
/** When the layout started, on the same clock. */
|
|
111
|
+
startedAt() { return contextOf(this)?.startedAt() ?? this.now(); }
|
|
112
|
+
get timeZone() { return contextOf(this)?.timeZone ?? Intl.DateTimeFormat().resolvedOptions().timeZone; }
|
|
113
|
+
get locale() { return contextOf(this)?.locale ?? "en-US"; }
|
|
114
|
+
/** Says the app may be shown; once. Within 5 s of its start, or the screen reports it late. */
|
|
115
|
+
ready() {
|
|
116
|
+
if (__classPrivateFieldGet(this, _WyndlassApp_said, "f"))
|
|
117
|
+
return;
|
|
118
|
+
__classPrivateFieldSet(this, _WyndlassApp_said, true, "f");
|
|
119
|
+
contextOf(this)?.ready();
|
|
120
|
+
}
|
|
121
|
+
update(epochMs) {
|
|
122
|
+
if (!__classPrivateFieldGet(this, _WyndlassApp_mounted, "f"))
|
|
123
|
+
return;
|
|
124
|
+
this.draw(epochMs);
|
|
125
|
+
__classPrivateFieldGet(this, _WyndlassApp_instances, "m", _WyndlassApp_present).call(this, epochMs);
|
|
126
|
+
}
|
|
127
|
+
// Every call is kept (ADR 0071): a paged app is given an exit and an enter at each page turn, all at the start.
|
|
128
|
+
enter(o) { __classPrivateFieldSet(this, _WyndlassApp_hooks, [...__classPrivateFieldGet(this, _WyndlassApp_hooks, "f"), { ...o, kind: "enter" }].sort((a, b) => a.startAt - b.startAt), "f"); this.update(this.now()); }
|
|
129
|
+
exit(o) { __classPrivateFieldSet(this, _WyndlassApp_hooks, [...__classPrivateFieldGet(this, _WyndlassApp_hooks, "f"), { ...o, kind: "exit" }].sort((a, b) => a.startAt - b.startAt), "f"); this.update(this.now()); }
|
|
130
|
+
/** Forgets the enters and exits given so far (the designer gives them again when the clips change). */
|
|
131
|
+
reset() { __classPrivateFieldSet(this, _WyndlassApp_hooks, [], "f"); this.update(this.now()); }
|
|
132
|
+
/** A paged app's items, for a staggered enter or exit (ADR 0071, decision 4); none animates the app as one box. */
|
|
133
|
+
staggered() { return []; }
|
|
134
|
+
}
|
|
135
|
+
_WyndlassApp_sized = function _WyndlassApp_sized() {
|
|
136
|
+
this.style.setProperty("--w", `${this.clientWidth || 1}px`);
|
|
137
|
+
this.style.setProperty("--h", `${this.clientHeight || 1}px`);
|
|
138
|
+
}, _WyndlassApp_present = function _WyndlassApp_present(epochMs) {
|
|
139
|
+
const started = __classPrivateFieldGet(this, _WyndlassApp_hooks, "f").filter(h => h.startAt <= epochMs);
|
|
140
|
+
const h = started[started.length - 1];
|
|
141
|
+
const items = h?.staggerMs ? this.staggered() : [];
|
|
142
|
+
for (const item of __classPrivateFieldGet(this, _WyndlassApp_lastItems, "f"))
|
|
143
|
+
if (!items.includes(item))
|
|
144
|
+
Object.assign(item.style, { opacity: "", transform: "" });
|
|
145
|
+
__classPrivateFieldSet(this, _WyndlassApp_lastItems, items, "f");
|
|
146
|
+
if (!h) {
|
|
147
|
+
this.style.opacity = __classPrivateFieldGet(this, _WyndlassApp_hooks, "f").some(x => x.kind === "enter") ? "0" : "";
|
|
148
|
+
this.style.transform = "";
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
if (items.length) {
|
|
152
|
+
Object.assign(this.style, { opacity: "", transform: "" });
|
|
153
|
+
staggerTimes(h.startAt, h.durationMs, h.staggerMs, items.length).forEach((w, i) => Object.assign(items[i].style, look(h, w.startMs, w.endMs - w.startMs, epochMs)));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
Object.assign(this.style, look(h, h.startAt, h.durationMs, epochMs));
|
|
157
|
+
};
|
|
158
|
+
/** One preset's look at a moment: from its start, over its length, by its easing (the layout schema's names). */
|
|
159
|
+
function look(h, startAt, durationMs, epochMs) {
|
|
160
|
+
const raw = durationMs > 0 ? (epochMs - startAt) / durationMs : epochMs >= startAt ? 1 : 0;
|
|
161
|
+
const t = Math.min(1, Math.max(0, h.reducedMotion ? (raw >= 1 ? 1 : 0) : raw));
|
|
162
|
+
const eased = cubicBezier(easingOf(CAMEL[h.easing ?? ""] ?? h.easing ?? "ease-out"), t);
|
|
163
|
+
const shown = h.kind === "enter" ? eased : 1 - eased;
|
|
164
|
+
const transform = h.preset === "scale" ? `scale(${0.8 + 0.2 * shown})` : h.preset === "slide-up" ? `translateY(${(1 - shown) * 20}%)`
|
|
165
|
+
: h.preset === "slide-left" ? `translateX(${(1 - shown) * 20}%)` : "";
|
|
166
|
+
return { opacity: shown >= 1 ? "" : String(shown), transform };
|
|
167
|
+
}
|
|
168
|
+
/** The names Wyndlass's apps' release 1.2.0 read, for layouts saved with them. */
|
|
169
|
+
const CAMEL = { easeIn: "ease-in", easeOut: "ease-out", easeInOut: "ease-in-out" };
|
|
170
|
+
/** A size that fits the app's box: the smaller of a share of its height and a share of its width (--h and --w). */
|
|
171
|
+
export function fit(ofHeight, ofWidth) {
|
|
172
|
+
// Times the font's scale (--s, from the font prop): the size the person asked for, relative to the one the app chose.
|
|
173
|
+
return `calc(min(calc(var(--h) * ${ofHeight}), calc(var(--w) * ${ofWidth})) * var(--s, 1))`;
|
|
174
|
+
}
|
|
175
|
+
/** A typography value as CSS: its face, decoration, letter case, spacing, line height, shadow, and its scale as --s. */
|
|
176
|
+
export function typographyCss(value) {
|
|
177
|
+
const f = value && typeof value === "object" ? value : {};
|
|
178
|
+
const num = (v, lo, hi) => typeof v === "number" && Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : undefined;
|
|
179
|
+
const family = typeof f.family === "string" ? f.family.replace(/["\\;{}<>]/g, "") : "";
|
|
180
|
+
const out = [family ? `font-family:"${family}",system-ui,sans-serif` : "font-family:system-ui,sans-serif"];
|
|
181
|
+
out.push(`font-weight:${num(f.weight, 100, 900) ?? 400}`, `font-style:${f.style === "italic" ? "italic" : "normal"}`);
|
|
182
|
+
const lines = Array.isArray(f.decoration) ? f.decoration.filter(d => d === "underline" || d === "line-through" || d === "overline") : [];
|
|
183
|
+
if (lines.length)
|
|
184
|
+
out.push(`text-decoration-line:${lines.join(" ")}`);
|
|
185
|
+
if (f.transform === "uppercase" || f.transform === "lowercase" || f.transform === "capitalize")
|
|
186
|
+
out.push(`text-transform:${f.transform}`);
|
|
187
|
+
const spacing = num(f.letterSpacing, -0.5, 2);
|
|
188
|
+
if (spacing)
|
|
189
|
+
out.push(`letter-spacing:${spacing}em`);
|
|
190
|
+
const height = num(f.lineHeight, 0.5, 4);
|
|
191
|
+
if (height)
|
|
192
|
+
out.push(`line-height:${height}`);
|
|
193
|
+
const shadow = f.shadow && typeof f.shadow === "object" ? f.shadow : null;
|
|
194
|
+
if (shadow) {
|
|
195
|
+
const c = shadow.color?.value;
|
|
196
|
+
out.push(`text-shadow:${num(shadow.x, -200, 200) ?? 0}px ${num(shadow.y, -200, 200) ?? 2}px ${num(shadow.blur, 0, 200) ?? 4}px ${color(c, "#000000")}`);
|
|
197
|
+
}
|
|
198
|
+
out.push(`--s:${num(f.scale, 0.25, 4) ?? 1}`);
|
|
199
|
+
return out.join(";") + ";";
|
|
200
|
+
}
|
|
201
|
+
/** The colour props as CSS, with a fallback. */
|
|
202
|
+
export function color(v, fallback) {
|
|
203
|
+
return typeof v === "string" && /^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/.test(v) ? v : fallback;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Defines an element once, under its fixed tag, and returns the tag (the bundle's `element` export): the old shape, for
|
|
207
|
+
* players older than Player API 2, which only Wyndlass's own apps need. A newer host defines the default export under a
|
|
208
|
+
* tag of its own instead.
|
|
209
|
+
*/
|
|
210
|
+
export function define(tag, element) {
|
|
211
|
+
if (!customElements.get(tag))
|
|
212
|
+
customElements.define(tag, element);
|
|
213
|
+
return tag;
|
|
214
|
+
}
|
|
215
|
+
/** Text safe to put in markup. */
|
|
216
|
+
export function escape(s) {
|
|
217
|
+
return s.replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", "\"": """, "'": "'" })[c]);
|
|
218
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/* @wyndlass/app-sdk's preview: player-core's app frame, compiled. MIT, (c) Northwynd Labs LLC. */
|
|
2
|
+
"use strict";(()=>{var pt=Object.defineProperty;var lt=(e,t,n)=>t in e?pt(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var m=(e,t,n)=>lt(e,typeof t!="symbol"?t+"":t,n);var ve={apiVersion:2,properties:[{name:"device",description:"What the device is: id, type, shell and versions, model, resolution and orientation. Fixed while the player runs."},{name:"mode",description:"Where player-core runs: player, preview (in the CMS) or designer. In preview and the designer, commands change nothing."},{name:"timeZone",description:"The player's time zone (IANA), from the host.",event:"time.changed"},{name:"locale",description:"The player's locale (BCP 47), from the host."},{name:"connection",description:"Whether the player is connected to Wyndlass.",event:"connection.changed"},{name:"state",description:"starting, playing, paused, stopped (the standby screen), idle (nothing to play) or error.",event:"state.changed"},{name:"held",description:"A remote pause or stop in effect, by whom and since when; absent when none.",event:"state.changed"},{name:"content",description:"The manifest playing, when it was issued and switched to; absent when there is none.",event:"content.updated"},{name:"now",description:"What each zone shows: the item, its blob and content type, when it started, and its duration and position when known.",event:"item.start"},{name:"audio",description:"The player's volume (0 to 100) and whether it is muted; only when the host can set them.",event:"audio.changed"},{name:"display",description:"The display's power and brightness; only when the host can report them.",event:"display.changed"},{name:"capabilities",description:"What the host can do: audio, display power, display brightness."}],events:[{name:"state.changed",description:"The player's state changed (playing, paused, stopped, idle, error), and who changed it."},{name:"content.updated",description:"A new manifest was switched to."},{name:"item.start",description:"An item started in a zone (source start)."},{name:"item.end",description:"An item ended in a zone (source end): completed, skipped, stopped, error, replaced by new content, or finished by its app."},{name:"item.error",description:"An item could not be shown."},{name:"connection.changed",description:"The player connected to Wyndlass or lost its connection."},{name:"audio.changed",description:"The volume or mute changed, and who changed it."},{name:"display.changed",description:"The display's power or brightness changed, and who changed it."},{name:"time.changed",description:"The clock was adjusted or the time zone changed."},{name:"app.command",description:"A command for apps on the screen (apps.sendCommand); an app reacts if it knows the name."},{name:"app.failed",description:"An app on the screen could not run, or misbehaved (WP-0100): its bundle did not load, it was not an app, it needs a newer player, it kept throwing and was stopped, or it was late or overran a clip."},{name:"app.held",description:"An app version was held back on this screen until the next manifest, after the page stopped answering twice in an hour while it was on screen (WP-0100)."}],commands:[{name:"player.status",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/status",description:"Everything about the player now: device, mode, time zone, locale, connection, state (and any hold), the content playing, what each zone shows, audio, display and what the host can do. Changes nothing.",idempotent:!0},{name:"player.events",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/events",result:"player-api-results.schema.json#/$defs/events",description:"The player's recent events (the last 200), oldest first, after a sequence number: state changes, content updates, item starts and ends, errors, connection, audio, display and time. Pass next as after to poll for more. Changes nothing.",idempotent:!0},{name:"time.now",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/timeNow",description:"The player's current time, its time zone and UTC offset, and whether its clock is synchronized. Changes nothing.",idempotent:!0},{name:"player.play",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Resumes or starts playback on the screen. From a remote caller it also ends a remote pause or stop.",idempotent:!0},{name:"player.pause",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Freezes the screen on what it shows now: the loop's timer and any media stop where they are. From a remote caller it holds until a remote player.play or a restart; apps cannot resume it, and new content arrives paused.",idempotent:!0},{name:"player.stop",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Stops playback and shows the standby screen (black, display on). From a remote caller it holds until a remote player.play or a restart; apps cannot restart it, and new content arrives stopped.",idempotent:!0},{name:"player.next",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/zone",result:"player-api-results.schema.json#/$defs/now",description:"Skips to the next item in a zone (main by default); the current item ends as skipped."},{name:"player.previous",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/zone",result:"player-api-results.schema.json#/$defs/now",description:"Goes back to the previous item in a zone (main by default); the current item ends as skipped."},{name:"player.playItem",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/playItem",result:"player-api-results.schema.json#/$defs/now",description:"Shows a given item of the content playing now in a zone (main by default), then continues the loop from it. Refused for an item the playing manifest does not have."},{name:"audio.setVolume",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setVolume",result:"player-api-results.schema.json#/$defs/audio",description:"Sets the player's volume, 0 to 100. Only on players whose host can set it to a level.",needs:"audio.volume",idempotent:!0},{name:"audio.setMuted",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setMuted",result:"player-api-results.schema.json#/$defs/audio",description:"Mutes or unmutes the player. Only on players whose host can.",needs:"audio",idempotent:!0},{name:"display.setPower",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setPower",result:"player-api-results.schema.json#/$defs/display",description:"Turns the display on or to standby. Only on players whose host can. Standby blanks the screen until it is turned on.",needs:"display.power",idempotent:!0},{name:"display.setBrightness",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setBrightness",result:"player-api-results.schema.json#/$defs/display",description:"Sets the display's brightness, 0 to 100. Only on players whose host can.",needs:"display.brightness",idempotent:!0},{name:"apps.sendCommand",access:"control",apps:"none",remote:!0,params:"player-api-params.schema.json#/$defs/sendCommand",result:"player-api-results.schema.json#/$defs/delivered",description:"Sends a named command, with an optional argument, to the apps on the screen; an app that knows the name reacts. Returns how many apps listen."},{name:"app.finish",access:"control",apps:"read",remote:!1,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/none",description:"An app ends its own slot now: its item ends as finished and the loop moves on. Apps only; not a tool."}],host:[{name:"host.describe",kind:"request",description:"What the device is, its time zone, locale, connection and capabilities.",result:"player-api-host.schema.json#/$defs/describeResult"},{name:"host.time",kind:"request",description:"The host's time and time zone.",result:"player-api-common.schema.json#/$defs/timeNow"},{name:"host.getAudio",kind:"request",description:"The player's volume and mute.",result:"player-api-common.schema.json#/$defs/audio"},{name:"host.setAudio",kind:"request",description:"Sets the volume, the mute or both; answers the audio after.",result:"player-api-common.schema.json#/$defs/audio"},{name:"host.getDisplay",kind:"request",description:"The display's power and brightness.",result:"player-api-common.schema.json#/$defs/display"},{name:"host.setDisplay",kind:"request",description:"Sets the display's power, brightness or both; answers the display after.",result:"player-api-common.schema.json#/$defs/display"},{name:"host.adRequest",kind:"request",description:"Asks Wyndlass's ad broker for one slot of a break over the host's NATS connection (WP-0075); the result is the broker's ad-decision (ad-decision.schema.json). Only a host whose describe says ads is asked."},{name:"host.mediaPrepare",kind:"request",description:"Lays a video out behind the page and prepares it to its first frame; host.mediaState says when (ADR 0055)."},{name:"host.mediaShow",kind:"request",description:"Shows and hides prepared videos; replies once its layer has changed."},{name:"host.mediaControl",kind:"request",description:"Plays, pauses or seeks a video, or asks for its position."},{name:"host.mediaRelease",kind:"request",description:"Removes a video and frees its decoder."},{name:"host.reply",kind:"reply",description:"The host's answer to a request: result or error (unsupported, failed)."},{name:"host.connection",kind:"event",description:"The connection to Wyndlass changed."},{name:"host.timeChanged",kind:"event",description:"The clock was adjusted or the time zone changed."},{name:"host.audioChanged",kind:"event",description:"The volume or mute changed outside the player."},{name:"host.displayChanged",kind:"event",description:"The display changed outside the player."},{name:"host.mediaState",kind:"event",description:"A video was prepared, started, ended or failed, or its position was asked for."},{name:"host.presence",kind:"event",description:"What one of the host's sensors sees now: the presences in view, with random ids that never leave the page (WP-0105)."},{name:"host.sensing",kind:"event",description:"The site's sensing mode as the host applies it, and its sensors (WP-0105)."},{name:"host.serialPorts",kind:"event",description:"The host's sensor controller ports, and which are open (WP-0043)."},{name:"host.serialLine",kind:"event",description:"One line from a sensor controller, as it arrived (WP-0043)."},{name:"host.serialWrite",kind:"request",description:"Writes lines to a sensor controller, a gap apart; replies once written (WP-0043)."},{name:"host.bleWatch",kind:"request",description:"What the page listens for: beacons' iBeacon UUIDs, Eddystone namespaces and addresses; replies once the scan follows it. Sent only to a host that has sent host.bleState (WP-0044)."},{name:"host.bleAdverts",kind:"event",description:"Beacon frames that match the watch, at most four a second of each kind from each beacon (WP-0044)."},{name:"host.bleState",kind:"event",description:"The host's Bluetooth scanner: scanning, idle, off, refused, missing or failed; sent only by a host that has one (WP-0044)."},{name:"host.gpioPins",kind:"event",description:"The GPIO pins the host reads, with their values (WP-0044)."},{name:"host.gpioEdge",kind:"event",description:"A GPIO pin changed (WP-0044)."},{name:"host.mcp",kind:"relay",description:"An MCP message from ctl.dev.{deviceId}, for player-core to answer."},{name:"core.ready",kind:"core",description:"player-core is ready to answer host.mcp; until then the shell answers -32002 itself."},{name:"core.mcpReply",kind:"core",description:"player-core's answer to a host.mcp, for the shell to send back."},{name:"core.sensors",kind:"core",description:"The page's sensor controllers, channels and inputs, and the beacons it hears, whenever they change, for the heartbeat and sensors.recent (WP-0043, WP-0044)."}]};var ke=Symbol.for("wyndlass.app.attach");var Re=Reflect.apply,re=Object.getOwnPropertyDescriptor,V=Object.getPrototypeOf,y=globalThis,a=(e,t,n)=>Re(e,t,n),v=(e,t)=>e?re(e,t)?.get:void 0,oe=(e,t)=>e?re(e,t)?.set:void 0,f=e=>y[e]?.prototype,Ce=(e,t,n)=>Re(e,t,n);var Cn=Date.now,Ln=new TextEncoder,On=new TextDecoder,jn=TextEncoder.prototype.encode,In=TextDecoder.prototype.decode;var Dn=v(V(Uint8Array.prototype),"length");var ut=Uint8Array,mt=e=>new ut(e),ft=[];for(let e=0;e<256;e++)ft[e]=(e<16?"0":"")+e.toString(16);var ht=Object.create(null);for(let e=0;e<64;e++)ht["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e]]=e;var Ae=y.crypto?.subtle,se=Ae?V(Ae):void 0,ne=y.crypto,gt=ne?V(ne).getRandomValues:void 0,Hn=se?.digest,Bn=se?.importKey,Wn=se?.sign;var Le=e=>a(gt,ne,[mt(e)]),Un={setTimeout:y.setTimeout,clearTimeout:y.clearTimeout,setInterval:y.setInterval,clearInterval:y.clearInterval};var qn=y.fetch;var B=f("MessagePort"),zn=B&&B.postMessage,Zn=oe(B,"onmessage"),_n=B&&B.start,ie=f("MessageEvent"),Kn=v(ie,"data"),Vn=v(ie,"ports"),Gn=v(ie,"source");var Oe=f("EventTarget"),Jn=Oe?.addEventListener,Yn=Oe?.removeEventListener;var Qn=y.Worker,Z=f("Worker"),Xn=Z&&Z.postMessage,er=oe(Z,"onmessage"),tr=oe(Z,"onerror");var xe=y.Blob,_=y.URL,Se=_?.createObjectURL,yt=_?.revokeObjectURL,ae=xe&&Se?{make:(e,t)=>a(Se,_,[new xe(e,{type:t})]),revoke:e=>a(yt,_,[e])}:void 0,H=y.customElements,de=H?V(H):void 0,bt=de?.define,wt=de?.get,pe=H&&de?{define:(e,t)=>a(bt,H,[e,t]),get:e=>a(wt,H,[e])}:void 0,vt=y.document,K=f("Document"),kt=K?.createElement,nr=K?.createElementNS,At=WeakSet.prototype.add,rr=WeakSet.prototype.has,xt=new WeakSet;var St=(e,t)=>(a(At,e,[t]),t);function je(e){return St(xt,a(kt,vt,[e]))}var W=f("Node"),le=f("Element"),G=f("HTMLElement"),j=e=>v(W,e),Ee=j("parentNode"),Et=j("firstChild"),Tt=j("lastChild"),Mt=j("nextSibling"),Ft=j("nodeType"),Pt=j("childNodes"),$t=v(le,"localName"),Nt=W?.appendChild,Rt=W?.insertBefore,Ct=W?.removeChild,C=le,Lt=C?.getAttribute,Ot=C?.getAttributeNames,jt=C?.setAttribute,It=C?.removeAttribute,Dt=C?.matches,Ht=C?.attachShadow,Bt=C?.querySelectorAll,Wt=f("HTMLDialogElement")?.close,Ut=v(f("NodeList"),"length"),qt=f("NodeList")?.item,J=f("MutationRecord"),zt=v(J,"type"),Zt=v(J,"target"),_t=v(J,"addedNodes"),Kt=v(J,"removedNodes"),Te=y.MutationObserver,Vt=f("MutationObserver")?.observe,Ie=K?re(K,"adoptedStyleSheets"):void 0,Me=Ie?.get,Fe=Ie?.set,Gt=y.requestAnimationFrame,Pe=v(G,"style"),Y=f("CSSStyleDeclaration"),Jt=Y?.getPropertyValue,Yt=Y?.getPropertyPriority,Qt=Y?.setProperty,$e=G?.showPopover,Ne=G?.hidePopover,Xt=v(f("Event"),"target"),en=f("Event")?.stopImmediatePropagation,z=e=>{let t=[];for(let n=0,r=a(Ut,e,[]);n<r;n++)t[n]=a(qt,e,[n]);return t},De=Te&&W&&le&&G&&Y?{parent:e=>a(Ee,e,[]),first:e=>a(Et,e,[]),last:e=>a(Tt,e,[]),next:e=>a(Mt,e,[]),isElement:e=>a(Ft,e,[])===1,name:e=>a($t,e,[]),children:e=>z(a(Pt,e,[])),append:(e,t)=>a(Nt,e,[t]),insert:(e,t,n)=>a(Rt,e,[t,n]),remove:e=>{let t=a(Ee,e,[]);t&&a(Ct,t,[e])},attribute:(e,t)=>a(Lt,e,[t]),attributes:e=>a(Ot,e,[]),setAttribute:(e,t,n)=>a(jt,e,[t,n]),removeAttribute:(e,t)=>a(It,e,[t]),matches:(e,t)=>a(Dt,e,[t]),all:(e,t)=>z(a(Bt,e,[t])),closeDialog:e=>a(Wt,e,[]),shadow:e=>a(Ht,e,[{mode:"closed"}]),style:(e,t)=>{let n=a(Pe,e,[]);return[a(Jt,n,[t]),a(Yt,n,[t])]},setStyle:(e,t,n,r="")=>a(Qt,a(Pe,e,[]),[t,n,r]),observer:e=>new Te(e),observe:(e,t,n)=>a(Vt,e,[t,n]),record:e=>({type:a(zt,e,[]),target:a(Zt,e,[]),added:z(a(_t,e,[])),removed:z(a(Kt,e,[]))}),adopted:Me&&Fe?{count:e=>a(Me,e,[]).length,clear:e=>a(Fe,e,[[]])}:void 0,frame:e=>a(Gt,globalThis,[e]),popover:$e&&Ne?{show:e=>a($e,e,[]),hide:e=>a(Ne,e,[])}:void 0,target:e=>a(Xt,e,[]),stop:e=>a(en,e,[])}:void 0;var M=class extends Error{constructor(n,r){super(`the app could not run (${n})`);m(this,"reason",n);m(this,"error",r)}};function L(e){try{let t=e instanceof Error?e.name:void 0;return typeof t=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(t)?t:void 0}catch{return}}function U(e){let t=e?.id;if(typeof t!="string"||t==="")return!0;for(let n=0;n<He.length;n++)if(t[n]!==He[n])return!1;return!0}var He="com.wyndlass.";function tn(e=typeof navigator>"u"?"":navigator.userAgent){let t=/Chrom(?:e|ium)\/(\d+)/.exec(e);return t?Number(t[1]):void 0}function Be(e,t=ve.apiVersion,n=tn()){return!(e?.playerApi!==void 0&&e.playerApi>t||e?.webView!==void 0&&n!==void 0&&e.webView>n)}function nn(e){let t;try{t=e.paging?.()}catch{return null}let n=t;return n&&Number.isInteger(n.count)&&n.count>1&&typeof n.ms=="number"&&n.ms>0?{count:n.count,ms:n.ms}:null}var ce=new Map;function rn(){let e=Le(8),t="";for(let n=0;n<8;n++)t+=on[e[n]%26];return t}var on="abcdefghijklmnopqrstuvwxyz",sn=0;function We(e,t,n){let r=ce.get(e);if(!r){let o=ae.make([t],"text/javascript");r=import(o).then(s=>an(s,n),s=>{throw s instanceof M?s:new M("load",L(s))}).finally(()=>ae.revoke(o)),ce.set(e,r),r.catch(()=>ce.delete(e))}return r}function an(e,t){let n=e.default;if(typeof n=="function"&&n.prototype instanceof HTMLElement){let r=n.prototype[ke];if(typeof r!="function")throw new M("shape");let o=class extends n{},s=`${t?.element??"wl-app"}-${rn()}${++sn}`;try{pe.define(s,o)}catch(c){throw new M("shape",L(c))}return{tag:s,shape:"class",attach:(c,k)=>{Ce(r,c,[k])}}}if(U(t)&&typeof e.element=="string"&&pe.get(e.element))return{tag:e.element,shape:"legacy"};throw new M("shape")}var dn=3,pn=6e4,Q=class{constructor(t,n){m(this,"element",t);m(this,"o",n);m(this,"faults",[]);m(this,"halted",!1)}get stopped(){return this.halted}has(t){try{return!this.halted&&typeof this.element[t]=="function"}catch{return!1}}update(t){this.halted||this.run(()=>this.element.update?.(t))}reset(){this.halted||this.run(()=>this.element.reset?.())}paging(){return this.halted?null:nn(this.element)}hook(t,n){if(this.halted)return Promise.resolve();let r;try{r=this.element[t]?.(n)}catch(o){return this.fault(o),Promise.resolve()}return Promise.resolve(r).then(()=>{},o=>this.fault(o))}stop(t){if(!this.halted){this.halted=!0;try{this.element.remove()}catch{}this.o.dispose?.(),this.o.onStop(t)}}dispose(){this.o.dispose?.()}run(t){try{t()}catch(n){this.fault(n)}}fault(t){let n=(this.o.now??Date.now)();this.faults=this.faults.filter(r=>n-r<pn),this.faults.push(n),this.faults.length>=dn&&this.stop(L(t))}},dr=24*3600*1e3;var X="wyndlass.frame";function ze(e,t){return!!e&&(e.apps?e.apps(t):!U(t))}var ln=new Set(["load","shape","unsupported","error","late","overran"]),Ue=50,qe=250,ue=class{constructor(t,n,r,o){m(this,"frame",t);m(this,"port",n);m(this,"o",o);m(this,"halted",!1);m(this,"pages");m(this,"hooks");m(this,"pending",new Map);m(this,"next",0);m(this,"told");m(this,"last");m(this,"quiet");this.hooks=new Set(r.hooks),this.pages=r.paging}get stopped(){return this.halted}has(t){return!this.halted&&this.hooks.has(t)}paging(){return this.halted?null:this.pages}update(t){if(this.halted)return;let n=performance.now(),r=this.last;this.last={t,at:n};let o=this.told;if(o&&(o.running?Math.abs(t-(o.t+(n-o.at)))<=Ue:t===o.t)){o.running&&this.watch();return}let s=!!r&&n-r.at<qe&&t>r.t&&Math.abs(t-r.t-(n-r.at))<=Ue;this.told={t,at:n,running:s},this.post({type:"clock",t,running:s}),s&&this.watch()}reset(){this.halted||this.post({type:"reset"})}hook(t,n){if(this.halted)return Promise.resolve();let r=++this.next;return new Promise(o=>{this.pending.set(r,o),this.post({type:"hook",id:r,kind:t,options:n})})}stop(t){this.halted||(this.halted=!0,this.frame.remove(),this.dispose(),this.o.onStop(t))}dispose(){clearTimeout(this.quiet),this.port.close();for(let t of this.pending.values())t();this.pending.clear()}heard(t){t.type==="paging"?this.pages=_e(t.paging):t.type==="hooked"&&typeof t.id=="number"?(this.pending.get(t.id)?.(),this.pending.delete(t.id)):t.type==="stopped"&&this.stop(Ke(t.error))}post(t){try{this.port.postMessage(t)}catch{}}watch(){clearTimeout(this.quiet),this.quiet=setTimeout(()=>{!this.told?.running||!this.last||this.halted||(this.told={...this.last,running:!1},this.post({type:"clock",t:this.last.t,running:!1}))},qe)}};async function Ze(e,t,n,r,o,s,c,k,h,i){let l=e.appInfo??n.info,u;try{u=new URL(h.frames).origin}catch{return i("unsupported")}let p=document.createElement("iframe");p.setAttribute("sandbox","allow-scripts"),p.setAttribute("referrerpolicy","no-referrer"),p.title=l?.id??"app",p.tabIndex=-1,p.dataset.frame=l?.id??"",p.style.cssText="display:block;width:100%;height:100%;border:0;pointer-events:none;background:transparent;color-scheme:normal";let b=new Promise(S=>{p.addEventListener("load",()=>S(!0),{once:!0}),setTimeout(()=>S(!1),s)});p.src=`${u}/`,t.append(p);let x=(S,w)=>(p.remove(),i(S,w));if(!await b||!p.contentWindow)return x("load");let{port1:A,port2:q}=new MessageChannel,N={type:"init",element:{id:e.id,type:"app",app:e.app,props:e.props??{},...e.binding?{binding:e.binding}:{},...e.appInfo?{appInfo:e.appInfo}:{}},bundle:{hash:n.hash,bytes:n.bytes.slice(),...n.info?{info:n.info}:{}},...r.place?{place:r.place}:{},screen:r.screen??null,timeZone:r.timeZone,locale:r.locale,preview:!!r.preview,clock:{t:o.now(),running:!1,startedAt:o.startedAt()},fonts:mn(e.props??{},h),snapshots:fn(e,r),deadlineMs:s};return new Promise(S=>{let w,P=!1,E=I=>{P||(P=!0,clearTimeout(T),S(I))},T=setTimeout(()=>{w?(E({app:w,late:!0}),w.update(o.now())):(A.close(),E(x("load")))},s+1e3),R=!1;A.onmessage=I=>{let d=I.data;if(!(!d||typeof d!="object")){if(d.type==="ask"){R&&cn(d,r,A);return}if(w){d.type==="ready"?(E({app:w,late:d.late===!0}),w.update(o.now())):w.heard(d);return}d.type==="hello"&&!R?(R=!0,A.postMessage(N,[N.bundle.bytes.buffer])):d.type==="made"&&Array.isArray(d.hooks)?(w=new ue(p,A,{type:"made",hooks:d.hooks.filter(g=>g==="enter"||g==="exit"),paging:_e(d.paging)},{onStop:k}),c(w)):d.type==="failed"&&(A.close(),E(x(ln.has(d.reason)?d.reason:"error",Ke(d.error))))}},p.contentWindow.postMessage({type:X},"*",[q])})}async function cn(e,t,n){if(typeof e.id!="number")return;let r=(o,s=[])=>{try{n.postMessage(o,s)}catch{}};try{if(!t.data)throw new Error("no data service");if(e.what==="data"&&typeof e.source=="string")r({type:"answer",id:e.id,value:await t.data.get(e.source,un(e.params))});else if(e.what==="image"&&typeof e.path=="string"){let o=await t.data.imageBlob(e.path),s=new Uint8Array(await o.arrayBuffer());r({type:"answer",id:e.id,value:{bytes:s,type:o.type}},[s.buffer])}else throw new TypeError("not a question")}catch(o){r({type:"answer",id:e.id,error:L(o)??"Error"})}}function un(e){let t={};if(e&&typeof e=="object")for(let[n,r]of Object.entries(e))(typeof r=="string"||typeof r=="number"||typeof r=="boolean")&&(t[n]=r);return t}function _e(e){let t=e;return t&&Number.isInteger(t.count)&&t.count>1&&typeof t.ms=="number"&&t.ms>0?{count:t.count,ms:t.ms}:null}function Ke(e){return typeof e=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(e)?e:void 0}function mn(e,t){let n=new Set;for(let o of Object.values(e)){let s=o?.family;o&&typeof o=="object"&&typeof s=="string"&&n.add(s)}let r=[];for(let o of t.fonts??[]){let s=n.has(o.family)?t.font?.(o.blob):void 0;s&&r.push({family:o.family,weight:o.weight,style:o.style,blob:o.blob,bytes:s.slice()})}return r}function fn(e,t){let n=new Set;e.binding?.table&&n.add(e.binding.table);for(let o of Object.values(e.props??{}))typeof o=="string"&&/^tbl_[0-9a-z]{26}$/.test(o)&&n.add(o);let r={};for(let o of n){let s=t.snapshot?.(o);s&&(r[o]=s)}return r}function Ve(e){let t=De?.setStyle??((n,r,o,s)=>n.style.setProperty(r,o,s));t(e,"contain","strict","important"),t(e,"overflow","clip","important")}var hn=["name","type","tags","location","site","district","region","organization","path","timeZone"],gn=["text","date","currency","truncate"],Ge={site:"SITE",district:"DISTRICT",region:"REGION",organization:"ORGANIZATION"};function ee(e,t,n,r){if(!e)return n==="timeZone"&&t?t:void 0;if(n==="fields")return r&&e.fields?.[r]||void 0;let o=e.path??[];switch(n){case"name":return e.name||void 0;case"type":return e.type||void 0;case"tags":return e.tags?.length?e.tags.join(", "):void 0;case"location":return o.at(-1)?.name;case"path":return o.length?o.map(s=>s.name).join(" \u203A "):void 0;case"timeZone":return t||void 0;default:return Ge[n]?[...o].reverse().find(s=>s.kind===Ge[n])?.name:void 0}}function yn(e,t){if(e==null||e==="")return;let n=String(e);if(t==="currency"){let r=Number(e);return Number.isFinite(r)?r.toFixed(2):n}if(t==="date"){let r=Date.parse(n);return Number.isNaN(r)?n:new Date(r).toISOString().slice(0,10)}return t==="truncate"&&n.length>40?n.slice(0,39)+"\u2026":n}function Je(e,t){return(e.source==="screen"?ee(t.screen,t.timeZone,e.field,e.key):yn(t.tables?.[e.table]?.rows?.[e.row??0]?.[e.column],e.format))??e.fallback??""}function bn(e){let t=/^\s*(?:screen\.(fields\.[a-z][A-Za-z0-9]{0,31}|[A-Za-z]+)|table\.([A-Za-z0-9_-]+)\[(\d+)\](?:\.([A-Za-z0-9_]+)|\["((?:[^"\\]|\\.)*)"\]))\s*((?:\|\s*(?:[a-z]+|"(?:[^"\\]|\\.)*")\s*)*)$/.exec(e);if(!t)return null;let n=[...t[6].matchAll(/\|\s*([a-z]+|"((?:[^"\\]|\\.)*)")/g)],r=n.find(s=>s[1].startsWith('"'))?.[2]?.replace(/\\(.)/g,"$1"),o=n.find(s=>!s[1].startsWith('"'))?.[1];return t[1]!==void 0?o?null:t[1].startsWith("fields.")?{source:"screen",field:"fields",key:t[1].slice(7),...r!==void 0?{fallback:r}:{}}:hn.includes(t[1])?{source:"screen",field:t[1],...r!==void 0?{fallback:r}:{}}:null:o&&!gn.includes(o)?null:{source:"table",table:t[2],row:Number(t[3]),column:t[4]??t[5].replace(/\\(.)/g,"$1"),...o?{format:o}:{},...r!==void 0?{fallback:r}:{}}}function Ye(e,t){return e.replace(/\{{2}([^{}]*)\}{2}/g,(n,r)=>{let o=bn(r);return o?Je(o,t):n})}function Qe(e,t){return Object.fromEntries(Object.entries(e).map(([n,r])=>[n,typeof r=="string"?Ye(r,t):r]))}var me=e=>e==null||e==="";function et(e,t,n,r,o,s){return wn(e,t,n,r,{now:o,timeZone:s})}function wn(e,t,n,r,o){if(n===null&&(t==="eq"||t==="ne"))return t==="eq"?me(e):!me(e);if(me(e)||n===null||n===void 0)return!1;switch(r){case"number":case"currency":{let s=Number(e),c=Number(n);return typeof n!="boolean"&&n!==""&&Number.isFinite(s)&&Number.isFinite(c)&&fe(t,s-c)}case"boolean":{let s=n===!0||n==="true"?!0:n===!1||n==="false"?!1:void 0;return s!==void 0&&(t==="eq"?e===s:t==="ne"?e!==s:!1)}case"date":{let s=n==="today"?vn(o.now,o.timeZone).date:String(n),c=String(e).slice(0,10);return/^\d{4}-\d{2}-\d{2}$/.test(s)&&fe(t,c<s?-1:c>s?1:0)}case"datetime":{let s=Date.parse(String(e)),c=n==="now"?o.now:Date.parse(String(n));return!Number.isNaN(s)&&!Number.isNaN(c)&&fe(t,s-c)}default:{let s=String(e).toLowerCase(),c=String(n).toLowerCase();return t==="eq"?s===c:t==="ne"?s!==c:t==="contains"?s.includes(c):!1}}}function fe(e,t){switch(e){case"eq":return t===0;case"ne":return t!==0;case"lt":return t<0;case"lte":return t<=0;case"gt":return t>0;case"gte":return t>=0;default:return!1}}var Xe=new Map;function vn(e,t){let n=Xe.get(t);n||(n=new Intl.DateTimeFormat("en-US",{timeZone:t,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",weekday:"short"}),Xe.set(t,n));let r=Object.fromEntries(n.formatToParts(new Date(e)).map(o=>[o.type,o.value]));return{date:`${r.year}-${r.month}-${r.day}`,minute:Number(r.hour)%24*60+Number(r.minute),day:String(r.weekday).slice(0,3).toLowerCase()}}var tt=e=>!!(e?.filter?.length||e?.sort||e?.limit),kn=e=>e==null||e==="";function An(e,t){if(!kn(e))switch(t){case"number":case"currency":{let n=Number(e);return Number.isFinite(n)?n:void 0}case"datetime":{let n=Date.parse(String(e));return Number.isNaN(n)?void 0:n}case"boolean":return e===!0||String(e).toLowerCase()==="true"?1:0;case"date":return String(e).slice(0,10);default:return String(e).toLowerCase()}}function xn(e,t){let n=Array.from(e),r=Array.from(t);for(let o=0;o<n.length&&o<r.length;o++){let s=n[o].codePointAt(0)-r[o].codePointAt(0);if(s!==0)return s}return n.length-r.length}function Sn(e,t,n,r,o){let s=e,c=Array.isArray(s?.rows)?s.rows:[],k=Array.isArray(s?.columns)?s.columns:void 0,h=i=>k?.find(l=>l.key===i)?.type??"text";for(let i of t.filter??[]){if(k&&!k.some(p=>p.key===i.column))return[];let l=i.value;if(l!==null&&typeof l=="object"){let p=l;if(l=ee(n,r,p.screen,p.key),l===void 0)return[]}let u=h(i.column);c=c.filter(p=>et(p[i.column],i.op,l,u,o,r))}if(t.sort){let{column:i}=t.sort,l=h(i),u=t.sort.order==="desc"?-1:1;c=c.map((p,b)=>({row:p,i:b,key:An(p[i],l)})).sort((p,b)=>{if(p.key===void 0||b.key===void 0)return p.key===void 0&&b.key===void 0?p.i-b.i:p.key===void 0?1:-1;let x=typeof p.key=="number"&&typeof b.key=="number"?p.key-b.key:xn(String(p.key),String(b.key));return x===0?p.i-b.i:x<0?-u:u}).map(p=>p.row)}return t.limit?c.slice(0,t.limit):c}function nt(e,t,n,r,o){return!e||typeof e!="object"?e:{...e,rows:Sn(e,t,n,r,o)}}function En(e){return{screen:e?.screen??null,timeZone:e?.timeZone,tables:new Proxy({},{get:(t,n)=>typeof n=="string"?e?.snapshot?.(n):void 0})}}async function ot(e,t,n,r,o,s,c,k=()=>{},h){let i=e.appInfo??n.info;Ve(t),i?.id&&(t.dataset.app=i.id);let l=(d,g)=>(t.dataset.appFailed=d,{failed:d,...g?{error:g}:{}});if(!Be(i?.requires))return l("unsupported");let u=r.appHost;if(u&&!u.custom&&!U(i))return l("unsupported");if(ze(h,i))return Ze(e,t,n,r,o,s,c,k,h,l);let p=e.app;if(u?.held?.(p))return t.dataset.appHeld="",{held:!0};await u?.mounting?.(p,i?.id);let b=!1,x=()=>{b||(b=!0,u?.unmounted?.(p))},A;try{A=await We(n.hash,n.bytes,i)}catch(d){return x(),d instanceof M?l(d.reason,d.error):l("load")}let q=A.tag,N=i?.id?r.controllerFor?.({id:i.id,instance:e.id,capability:i.player??"read"}):void 0,S,w=new Promise(d=>{S=d}),P,E={app:i?.id??e.app.id,element:q,props:Qe(e.props??{},En(r)),place:r.place,screen:r.screen??null,timeZone:r.timeZone??Intl.DateTimeFormat().resolvedOptions().timeZone,locale:r.locale??navigator.language,preview:!!r.preview,now:o.now,startedAt:o.startedAt,ready:()=>S(),data:(d,g)=>r.data?r.data.get(d,g):Promise.reject(new Error("no data service")),kept:(d,g)=>r.data?.kept?.(d,g),image:d=>r.data?r.data.image(d):Promise.reject(new Error("no data service")),snapshot:d=>{let g=r.snapshot?.(d)??null;if(!g||e.binding?.table!==d||!tt(e.binding))return g;let D=nt(g,e.binding,r.screen,E.timeZone,o.now());return P?.from===g&&P.rows.length===D.rows.length&&P.rows.every((at,dt)=>at===D.rows[dt])?P.snapshot:(P={from:g,rows:D.rows,snapshot:D},D)},...e.binding?{binding:e.binding}:{},...e.sensors?.includes("impressions")&&r.impressions?.measuring?{impressions:{now:()=>r.impressions.now(),on:d=>r.impressions.on(d)}}:{},...N?{controller:N}:{}},T;try{T=je(q),A.shape==="class"?A.attach(T,E):T.wyndlass=E}catch(d){return N?.dispose(),x(),l("error",L(d))}T.style.cssText="display:block;width:100%;height:100%",u?.contained?.(t,T);let R=new Q(T,{onStop:k,dispose:()=>{N?.dispose(),x()}});t.append(T),c(R);let I=await Promise.race([w.then(()=>!1),new Promise(d=>setTimeout(()=>d(!0),s))]);return R.update(E.now()),{app:R,late:I}}var rt=new Set;async function st(e,t){for(let n of e){let r=`${n.family}/${n.weight??400}/${n.style??"normal"}/${n.blob}`,o=t(n.blob);if(!(rt.has(r)||!o||typeof FontFace>"u"))try{let s=new FontFace(n.family,o,{weight:String(n.weight??400),style:n.style??"normal"});document.fonts.add(await s.load()),rt.add(r)}catch{}}}var te,F,O={t:0,at:0,running:!1},he={},ge="null",ye=new Map,Tn=0,$=e=>te.postMessage(e),be=()=>O.running?O.t+(performance.now()-O.at):O.t;addEventListener("message",e=>{te||e.source!==parent||e.data?.type!==X||!e.ports[0]||(te=e.ports[0],te.onmessage=t=>Mn(t.data),$({type:"hello"}))});function Mn(e){switch(e.type){case"init":Fn(e);break;case"clock":O={t:e.t,at:performance.now(),running:e.running},F?.update(be()),we();break;case"hook":{let t=e.id;(F?F.hook(e.kind,e.options):Promise.resolve()).then(()=>$({type:"hooked",id:t}));break}case"reset":F?.reset();break;case"snapshots":he=e.snapshots;break;case"answer":{let t=ye.get(e.id);ye.delete(e.id),e.error!==void 0?t?.reject(Object.assign(new Error("the host could not answer"),{name:e.error})):t?.resolve(e.value);break}}}function it(e){let t=++Tn;return new Promise((n,r)=>{ye.set(t,{resolve:n,reject:r}),$({type:"ask",id:t,...e})})}function we(){let e=F?.paging()??null,t=JSON.stringify(e);t!==ge&&(ge=t,$({type:"paging",paging:e}))}async function Fn(e){if(F)return;O={t:e.clock.t,at:performance.now(),running:e.clock.running},he=e.snapshots;let t=new Map,n=(i,l)=>`${i}?${JSON.stringify(Object.keys(l).sort().map(u=>[u,l[u]]))}`,o={data:{get:async(i,l)=>{let u=await it({what:"data",source:i,params:l});return t.set(n(i,l),u),u},kept:(i,l)=>t.get(n(i,l)),image:async i=>{let{bytes:l,type:u}=await it({what:"image",path:i});return URL.createObjectURL(new Blob([l],{type:u}))}},place:e.place,screen:e.screen,timeZone:e.timeZone,locale:e.locale,preview:e.preview,snapshot:i=>he[i]??null},s={now:be,startedAt:()=>e.clock.startedAt},c=new Map(e.fonts.map(i=>[i.blob,i.bytes]));await st(e.fonts,i=>c.get(i));let k=document.getElementById("app"),h=await ot(e.element,k,e.bundle,o,s,e.deadlineMs,i=>{F=i,ge=JSON.stringify(i.paging()),$({type:"made",hooks:["enter","exit"].filter(l=>i.has(l)),paging:i.paging()})},i=>$({type:"stopped",...i?{error:i}:{}}));"failed"in h?$({type:"failed",reason:h.failed,...h.error?{error:h.error}:{}}):"app"in h&&($({type:"ready",late:h.late}),we(),Pn())}function Pn(){let e=0,t=n=>{F?.stopped||(O.running&&F?.update(be()),n-e>1e3&&(e=n,we()),requestAnimationFrame(t))};requestAnimationFrame(t)}})();
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>App</title>
|
|
6
|
+
<!-- An app's frame in the CMS (WP-0100 decision 11). Its policy is the frames host's header (the CMS image), not a meta.
|
|
7
|
+
A classic script: this document's origin is opaque, so a module would be fetched with CORS. -->
|
|
8
|
+
<style>:root{color-scheme:normal}html,body,#app{margin:0;width:100%;height:100%;overflow:hidden;background:transparent}</style>
|
|
9
|
+
<script src="./frame.js" defer></script>
|
|
10
|
+
</head>
|
|
11
|
+
<body><div id="app"></div></body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Wyndlass app preview</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root { color-scheme: dark; --line: #2c3540; --muted: #93a1b0; --accent: #4f8cff; --danger: #ff6b6b; --ok: #4fd18b; --warn: #f5b84a; }
|
|
9
|
+
* { box-sizing: border-box; }
|
|
10
|
+
html, body { margin: 0; height: 100%; background: #12171d; color: #e6edf3; font: 14px/1.4 system-ui, sans-serif; }
|
|
11
|
+
body { display: grid; grid-template-columns: 330px 1fr; grid-template-rows: auto 1fr auto; height: 100vh; }
|
|
12
|
+
header { grid-column: 1 / 3; display: flex; gap: 12px; align-items: baseline; padding: 10px 16px; border-bottom: 1px solid var(--line); }
|
|
13
|
+
header h1 { font-size: 16px; margin: 0; }
|
|
14
|
+
header span { color: var(--muted); }
|
|
15
|
+
#controls { grid-row: 2 / 4; overflow: auto; border-right: 1px solid var(--line); padding: 8px 14px 24px; }
|
|
16
|
+
#controls h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 18px 0 8px; }
|
|
17
|
+
.field { display: flex; flex-direction: column; gap: 3px; margin-bottom: 8px; }
|
|
18
|
+
.field.inline { flex-direction: row; align-items: center; gap: 8px; }
|
|
19
|
+
.row { display: flex; gap: 8px; align-items: end; flex-wrap: wrap; }
|
|
20
|
+
.row .field { flex: 1; min-width: 70px; }
|
|
21
|
+
label { color: var(--muted); font-size: 12px; }
|
|
22
|
+
input, select, textarea, button { font: inherit; color: inherit; background: #1b222b; border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px; }
|
|
23
|
+
textarea { min-height: 70px; font: 12px/1.35 ui-monospace, monospace; }
|
|
24
|
+
button { cursor: pointer; }
|
|
25
|
+
button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
|
|
26
|
+
#stage { position: relative; overflow: hidden; background: repeating-conic-gradient(#1a2028 0 25%, #151a21 0 50%) 0 0 / 24px 24px; }
|
|
27
|
+
#report { border-top: 1px solid var(--line); padding: 8px 16px; display: flex; flex-wrap: wrap; gap: 6px 18px; align-items: baseline; min-height: 40px; }
|
|
28
|
+
#report .ok { color: var(--ok); } #report .late, #report .warn { color: var(--warn); } #report .failed, #controls .failed { color: var(--danger); }
|
|
29
|
+
#report pre { margin: 0; white-space: pre-wrap; color: var(--danger); }
|
|
30
|
+
#clock-time { font-variant-numeric: tabular-nums; }
|
|
31
|
+
</style>
|
|
32
|
+
</head>
|
|
33
|
+
<body>
|
|
34
|
+
<header><h1 id="title">Wyndlass app preview</h1><span id="subtitle"></span></header>
|
|
35
|
+
<aside id="controls" aria-label="Controls"></aside>
|
|
36
|
+
<section id="stage" aria-label="The app, as a screen shows it"></section>
|
|
37
|
+
<section id="report" aria-label="What a screen would report" aria-live="polite"></section>
|
|
38
|
+
<script src="/page.js"></script>
|
|
39
|
+
</body>
|
|
40
|
+
</html>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/* @wyndlass/app-sdk's preview: player-core's host, compiled. MIT, (c) Northwynd Labs LLC. */
|
|
2
|
+
"use strict";(()=>{var Nn=Object.defineProperty;var On=(t,e,n)=>e in t?Nn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var h=(t,e,n)=>On(t,typeof e!="symbol"?e+"":e,n);var wt=Reflect.apply,Ne=Object.getOwnPropertyDescriptor,fe=Object.getPrototypeOf,R=globalThis,v=(t,e,n)=>wt(t,e,n),O=(t,e)=>t?Ne(t,e)?.get:void 0,Oe=(t,e)=>t?Ne(t,e)?.set:void 0,P=t=>R[t]?.prototype,vt=(t,e,n)=>wt(t,e,n);var Oi=Date.now,$i=new TextEncoder,Di=new TextDecoder,ji=TextEncoder.prototype.encode,Fi=TextDecoder.prototype.decode;var Hi=O(fe(Uint8Array.prototype),"length");var $n=Uint8Array,Dn=t=>new $n(t),jn=[];for(let t=0;t<256;t++)jn[t]=(t<16?"0":"")+t.toString(16);var Fn=Object.create(null);for(let t=0;t<64;t++)Fn["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[t]]=t;var lt=R.crypto?.subtle,$e=lt?fe(lt):void 0,Re=R.crypto,Hn=Re?fe(Re).getRandomValues:void 0,Bi=$e?.digest,Ui=$e?.importKey,qi=$e?.sign;var kt=t=>v(Hn,Re,[Dn(t)]),_i={setTimeout:R.setTimeout,clearTimeout:R.clearTimeout,setInterval:R.setInterval,clearInterval:R.clearInterval};var Wi=R.fetch;var ne=P("MessagePort"),zi=ne&&ne.postMessage,Vi=Oe(ne,"onmessage"),Ki=ne&&ne.start,De=P("MessageEvent"),Zi=O(De,"data"),Ji=O(De,"ports"),Yi=O(De,"source");var xt=P("EventTarget"),Xi=xt?.addEventListener,Gi=xt?.removeEventListener;var Qi=R.Worker,ue=P("Worker"),er=ue&&ue.postMessage,tr=Oe(ue,"onmessage"),nr=Oe(ue,"onerror");var pt=R.Blob,he=R.URL,ct=he?.createObjectURL,Bn=he?.revokeObjectURL,je=pt&&ct?{make:(t,e)=>v(ct,he,[new pt(t,{type:e})]),revoke:t=>v(Bn,he,[t])}:void 0,ee=R.customElements,Fe=ee?fe(ee):void 0,Un=Fe?.define,qn=Fe?.get,He=ee&&Fe?{define:(t,e)=>v(Un,ee,[t,e]),get:t=>v(qn,ee,[t])}:void 0,At=R.document,me=P("Document"),_n=me?.createElement,Wn=me?.createElementNS,zn=WeakSet.prototype.add,sr=WeakSet.prototype.has,Be=new WeakSet,Vn=new WeakSet,ge=(t,e)=>(v(zn,t,[e]),e);function D(t){return ge(Be,v(_n,At,[t]))}var ye=t=>ge(Be,v(Wn,At,["http://www.w3.org/2000/svg",t]));var K=t=>ge(Vn,t);var se=P("Node"),Ue=P("Element"),be=P("HTMLElement"),Z=t=>O(se,t),ut=Z("parentNode"),Kn=Z("firstChild"),Zn=Z("lastChild"),Jn=Z("nextSibling"),Yn=Z("nodeType"),Xn=Z("childNodes"),Gn=O(Ue,"localName"),Qn=se?.appendChild,es=se?.insertBefore,ts=se?.removeChild,q=Ue,ns=q?.getAttribute,ss=q?.getAttributeNames,is=q?.setAttribute,rs=q?.removeAttribute,os=q?.matches,as=q?.attachShadow,ds=q?.querySelectorAll,ls=P("HTMLDialogElement")?.close,ps=O(P("NodeList"),"length"),cs=P("NodeList")?.item,we=P("MutationRecord"),us=O(we,"type"),hs=O(we,"target"),ms=O(we,"addedNodes"),fs=O(we,"removedNodes"),ht=R.MutationObserver,gs=P("MutationObserver")?.observe,Mt=me?Ne(me,"adoptedStyleSheets"):void 0,mt=Mt?.get,ft=Mt?.set,ys=R.requestAnimationFrame,gt=O(be,"style"),ve=P("CSSStyleDeclaration"),bs=ve?.getPropertyValue,ws=ve?.getPropertyPriority,vs=ve?.setProperty,yt=be?.showPopover,bt=be?.hidePopover,ks=O(P("Event"),"target"),xs=P("Event")?.stopImmediatePropagation,ce=t=>{let e=[];for(let n=0,s=v(ps,t,[]);n<s;n++)e[n]=v(cs,t,[n]);return e},te=ht&&se&&Ue&&be&&ve?{parent:t=>v(ut,t,[]),first:t=>v(Kn,t,[]),last:t=>v(Zn,t,[]),next:t=>v(Jn,t,[]),isElement:t=>v(Yn,t,[])===1,name:t=>v(Gn,t,[]),children:t=>ce(v(Xn,t,[])),append:(t,e)=>v(Qn,t,[e]),insert:(t,e,n)=>v(es,t,[e,n]),remove:t=>{let e=v(ut,t,[]);e&&v(ts,e,[t])},attribute:(t,e)=>v(ns,t,[e]),attributes:t=>v(ss,t,[]),setAttribute:(t,e,n)=>v(is,t,[e,n]),removeAttribute:(t,e)=>v(rs,t,[e]),matches:(t,e)=>v(os,t,[e]),all:(t,e)=>ce(v(ds,t,[e])),closeDialog:t=>v(ls,t,[]),shadow:t=>v(as,t,[{mode:"closed"}]),style:(t,e)=>{let n=v(gt,t,[]);return[v(bs,n,[e]),v(ws,n,[e])]},setStyle:(t,e,n,s="")=>v(vs,v(gt,t,[]),[e,n,s]),observer:t=>new ht(t),observe:(t,e,n)=>v(gs,t,[e,n]),record:t=>({type:v(us,t,[]),target:v(hs,t,[]),added:ce(v(ms,t,[])),removed:ce(v(fs,t,[]))}),adopted:mt&&ft?{count:t=>v(mt,t,[]).length,clear:t=>v(ft,t,[[]])}:void 0,frame:t=>v(ys,globalThis,[t]),popover:yt&&bt?{show:t=>v(yt,t,[]),hide:t=>v(bt,t,[])}:void 0,target:t=>v(ks,t,[]),stop:t=>v(xs,t,[])}:void 0;function qe(t){if(ge(Be,t),te)for(let e of te.children(t))te.isElement(e)&&qe(e);return t}var _e=t=>new TextEncoder().encode(t);function As(t){let e=t instanceof Uint8Array?t:new Uint8Array(t),n="";for(let s of e)n+=s.toString(16).padStart(2,"0");return n}async function We(t){return As(await crypto.subtle.digest("SHA-256",t))}var Ms=["name","type","tags","location","site","district","region","organization","path","timeZone"],Es=["text","date","currency","truncate"],Et={site:"SITE",district:"DISTRICT",region:"REGION",organization:"ORGANIZATION"};function ke(t,e,n,s){if(!t)return n==="timeZone"&&e?e:void 0;if(n==="fields")return s&&t.fields?.[s]||void 0;let i=t.path??[];switch(n){case"name":return t.name||void 0;case"type":return t.type||void 0;case"tags":return t.tags?.length?t.tags.join(", "):void 0;case"location":return i.at(-1)?.name;case"path":return i.length?i.map(o=>o.name).join(" \u203A "):void 0;case"timeZone":return e||void 0;default:return Et[n]?[...i].reverse().find(o=>o.kind===Et[n])?.name:void 0}}function Ss(t,e){if(t==null||t==="")return;let n=String(t);if(e==="currency"){let s=Number(t);return Number.isFinite(s)?s.toFixed(2):n}if(e==="date"){let s=Date.parse(n);return Number.isNaN(s)?n:new Date(s).toISOString().slice(0,10)}return e==="truncate"&&n.length>40?n.slice(0,39)+"\u2026":n}function ze(t,e){return(t.source==="screen"?ke(e.screen,e.timeZone,t.field,t.key):Ss(e.tables?.[t.table]?.rows?.[t.row??0]?.[t.column],t.format))??t.fallback??""}function Ts(t){let e=/^\s*(?:screen\.(fields\.[a-z][A-Za-z0-9]{0,31}|[A-Za-z]+)|table\.([A-Za-z0-9_-]+)\[(\d+)\](?:\.([A-Za-z0-9_]+)|\["((?:[^"\\]|\\.)*)"\]))\s*((?:\|\s*(?:[a-z]+|"(?:[^"\\]|\\.)*")\s*)*)$/.exec(t);if(!e)return null;let n=[...e[6].matchAll(/\|\s*([a-z]+|"((?:[^"\\]|\\.)*)")/g)],s=n.find(o=>o[1].startsWith('"'))?.[2]?.replace(/\\(.)/g,"$1"),i=n.find(o=>!o[1].startsWith('"'))?.[1];return e[1]!==void 0?i?null:e[1].startsWith("fields.")?{source:"screen",field:"fields",key:e[1].slice(7),...s!==void 0?{fallback:s}:{}}:Ms.includes(e[1])?{source:"screen",field:e[1],...s!==void 0?{fallback:s}:{}}:null:i&&!Es.includes(i)?null:{source:"table",table:e[2],row:Number(e[3]),column:e[4]??e[5].replace(/\\(.)/g,"$1"),...i?{format:i}:{},...s!==void 0?{fallback:s}:{}}}function Ve(t,e){return t.replace(/\{{2}([^{}]*)\}{2}/g,(n,s)=>{let i=Ts(s);return i?ze(i,e):n})}function St(t,e){return Object.fromEntries(Object.entries(t).map(([n,s])=>[n,typeof s=="string"?Ve(s,e):s]))}var Ke=t=>t==null||t==="";function Pt(t,e,n,s,i,o){return Ps(t,e,n,s,{now:i,timeZone:o})}function Ps(t,e,n,s,i){if(n===null&&(e==="eq"||e==="ne"))return e==="eq"?Ke(t):!Ke(t);if(Ke(t)||n===null||n===void 0)return!1;switch(s){case"number":case"currency":{let o=Number(t),r=Number(n);return typeof n!="boolean"&&n!==""&&Number.isFinite(o)&&Number.isFinite(r)&&Ze(e,o-r)}case"boolean":{let o=n===!0||n==="true"?!0:n===!1||n==="false"?!1:void 0;return o!==void 0&&(e==="eq"?t===o:e==="ne"?t!==o:!1)}case"date":{let o=n==="today"?Ls(i.now,i.timeZone).date:String(n),r=String(t).slice(0,10);return/^\d{4}-\d{2}-\d{2}$/.test(o)&&Ze(e,r<o?-1:r>o?1:0)}case"datetime":{let o=Date.parse(String(t)),r=n==="now"?i.now:Date.parse(String(n));return!Number.isNaN(o)&&!Number.isNaN(r)&&Ze(e,o-r)}default:{let o=String(t).toLowerCase(),r=String(n).toLowerCase();return e==="eq"?o===r:e==="ne"?o!==r:e==="contains"?o.includes(r):!1}}}function Ze(t,e){switch(t){case"eq":return e===0;case"ne":return e!==0;case"lt":return e<0;case"lte":return e<=0;case"gt":return e>0;case"gte":return e>=0;default:return!1}}var Tt=new Map;function Ls(t,e){let n=Tt.get(e);n||(n=new Intl.DateTimeFormat("en-US",{timeZone:e,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",weekday:"short"}),Tt.set(e,n));let s=Object.fromEntries(n.formatToParts(new Date(t)).map(i=>[i.type,i.value]));return{date:`${s.year}-${s.month}-${s.day}`,minute:Number(s.hour)%24*60+Number(s.minute),day:String(s.weekday).slice(0,3).toLowerCase()}}var Lt=(t,e)=>!t||(t.from===void 0||t.from<=e)&&(t.until===void 0||e<t.until);var ie=class{constructor(e,n,s,i,o){h(this,"renderer",e);h(this,"wrap",i);h(this,"onShown",o);h(this,"timer");h(this,"turns",0);h(this,"turnAt");h(this,"left");h(this,"stopped",!1);h(this,"share");h(this,"pages");this.pages=n.pages??[],this.share=n.durationMs/Math.max(1,this.pages.length),this.turnAt=s+this.share,this.arm()}arm(){clearTimeout(this.timer);let e=this.pages.length;if(this.stopped||this.left!==void 0||e<2||!this.wrap&&this.turns+1>=e)return;let n=this.pages[(this.turns+1)%e];this.renderer.prepare?.(n).catch(()=>{}),this.timer=setTimeout(()=>{this.turn(n)},Math.min(Math.max(0,this.turnAt-Date.now()),2**31-1))}async turn(e){try{await this.renderer.show(e)}catch{}this.stopped||(this.turns++,this.turnAt+=this.share,this.onShown?.(e),this.arm())}pause(){this.stopped||this.left!==void 0||(clearTimeout(this.timer),this.left=Math.max(0,this.turnAt-Date.now()))}resume(){this.stopped||this.left===void 0||(this.turnAt=Date.now()+this.left,this.left=void 0,this.arm())}stop(){if(!this.stopped){this.stopped=!0,clearTimeout(this.timer);for(let e of this.pages)this.renderer.release?.(e)}}};var Je=class extends Error{constructor(n,s){super(s);h(this,"code",n);this.name="MediaError"}};function Ye(t){return t instanceof Je?{code:t.code.slice(0,64),message:t.message.slice(0,1e3)}:{code:"not-shown",message:String(t instanceof Error?t.message:t??"").slice(0,1e3)}}var It=t=>t.blob.contentType.startsWith("video/");var Xe="position:absolute;inset:0;width:100%;height:100%;object-fit:contain",Cs=1,xe=class{constructor(e,n={}){h(this,"container",e);h(this,"options",n);h(this,"prepared",new Map);h(this,"shown",null);h(this,"endListeners",new Set);h(this,"itemListeners",new Set);h(this,"appListeners",new Set);h(this,"held",null);h(this,"drawn",0);h(this,"shownHash",null);let s=n.media;s&&(document.documentElement.style.background="transparent",document.body.style.background="transparent",e.style.background="#000",s.onEnd((i,o)=>{let r=this.shown;if(r?.mediaId===i)for(let a of this.endListeners)a(r.slide,o)}))}onMediaEnd(e){this.endListeners.add(e)}onElementItem(e){this.itemListeners.add(e)}onApp(e){this.appListeners.add(e)}app(e){for(let n of this.appListeners)n(e);this.options.onApp?.(e)}prepare(e){let n=this.prepared.get(e);if(n)return n.ready;let s;try{s=this.make(e)}catch(i){return Promise.reject(i)}return this.prepared.set(e,s),s.ready.catch(()=>{this.prepared.get(e)===s&&(this.prepared.delete(e),this.dispose(s))}),s.ready}release(e){let n=this.prepared.get(e);n&&(this.prepared.delete(e),this.dispose(n))}async show(e){await this.prepare(e);let n=this.prepared.get(e);if(!n)throw new Error("the slide was released before it was shown");this.prepared.delete(e);let s=this.shown,i=this.options.media,o=s?Rt(e):void 0;if(n.kind==="layout"&&o===void 0){await H(()=>{s?this.container.insertBefore(n.el,s.el):this.container.append(n.el),this.shown=n,this.shownHash=e.blob.hash}),this.held=[],await n.view.start().catch(()=>{});let r=Promise.resolve();return await H(()=>{this.shown===n&&(n.el.style.visibility="",s?.el.remove(),i&&this.paint("transparent"),i&&s?.mediaId&&(r=i.show([],[s.mediaId],Date.now())))}),await r.catch(()=>{}),s&&this.dispose(s),this.releaseHeld(),"cut"}if(o!==void 0&&i&&n.kind==="native"&&s.kind==="native"){if(n.fadesIn&&!n.queued){let r=Promise.resolve();return await H(()=>{n.el.style.visibility="",this.container.insertBefore(n.el,s.el),r=i.show([n.mediaId],[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{}),Rs(o).then(async()=>{await i.show([],[s.mediaId],Date.now()).catch(()=>{}),s.el.remove(),this.dispose(s)}),"crossfade"}}else if(o!==void 0&&i&&n.kind==="native"){let r=Promise.resolve();return await H(()=>{n.el.style.visibility="",this.container.insertBefore(n.el,s.el),r=i.show([n.mediaId],[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{}),this.paint("transparent"),Nt(s.el,1,0,o).then(()=>{s.el.remove(),this.dispose(s)}),"crossfade"}else if(o!==void 0)return await H(()=>{n.el.style.visibility="",n.el.style.opacity="0",this.container.append(n.el),n.kind==="video"&&Ge(n.el),this.shown=n,this.shownHash=e.blob.hash}),n.view&&(this.held=[],n.view.start().then(()=>this.releaseHeld())),Nt(n.el,0,1,o).then(async()=>{s.el.remove(),this.shown===n&&this.paint(i?"#000":""),i&&s.mediaId&&await i.show([],[s.mediaId],Date.now()).catch(()=>{}),this.dispose(s)}),"crossfade";if(i&&n.kind==="native"){let r=Promise.resolve();await H(()=>{n.el.style.visibility="",s?this.container.insertBefore(n.el,s.el):this.container.prepend(n.el),r=i.show([n.mediaId],s?.mediaId?[s.mediaId]:[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{}),await H(()=>{this.shown===n&&(s?.el.remove(),this.paint("transparent"))})}else{let r=Promise.resolve();await H(()=>{n.el.style.visibility="",n.el.parentNode!==this.container&&this.container.append(n.el),s?.el.remove(),this.paint(i?"#000":""),i&&s?.mediaId&&(r=i.show([],[s.mediaId],Date.now())),n.kind==="video"&&Ge(n.el),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{})}return s&&this.dispose(s),"cut"}paint(e){this.options.background?this.options.background(e):this.container.style.background=e}standby(){let e=this.shown;this.shown=null,this.shownHash=null,this.paint(this.options.media?"#000":""),e&&(e.mediaId&&this.options.media?.show([],[e.mediaId],Date.now()).catch(()=>{}),this.dispose(e))}pause(){let e=this.shown;e?.view?.pause(),e?.kind==="video"?e.el.pause():e?.mediaId&&this.options.media?.control(e.mediaId,"pause").catch(()=>{})}resume(){let e=this.shown;e?.view?.resume(),e?.kind==="video"?Ge(e.el):e?.mediaId&&this.options.media?.control(e.mediaId,"play").catch(()=>{})}make(e){if(e.layout){let{doc:r,source:a,untilPass:d}=e.layout,l=K(D("div"));l.dataset.blob=e.blob.hash,l.style.cssText=`${Xe};visibility:hidden`,this.container.append(l);let c={slide:e,kind:"layout",el:l,ready:Promise.resolve()},p=y=>this.options.path?`${this.options.path}/${y}`:y,m=String(++this.drawn);return c.view=new J(r,a,l,{media:this.options.media,path:this.options.path,endsWith:d?p(d):void 0,onItem:y=>{let A={...y,slide:y.slide?`${m}.${y.slide}`:m};this.held?this.held.push(A):this.item(A)},onPassEnd:y=>{if(d&&y===p(d)&&this.shown===c){for(let A of this.endListeners)A(e);return"hold"}return this.options.onPassEnd?.(y)},onApp:y=>this.app(y)}),c.ready=c.view.ready,c}if(!It(e)){if(!e.bytes&&!e.url)throw new Error(`no bytes for ${e.blob.hash}`);let r=e.bytes?URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})):void 0,a=K(D("img"));return a.src=r??e.url,a.alt="",a.dataset.blob=e.blob.hash,a.style.cssText=this.fill(),{slide:e,kind:"image",el:a,objectUrl:r,ready:a.decode()}}let n=this.options.media;if(n){let r=`m-${Cs++}`,a=K(D("div"));a.dataset.blob=e.blob.hash,a.dataset.media=r,a.style.cssText=`${Xe};visibility:hidden`;let d=this.container.getBoundingClientRect(),l={x:d.left,y:d.top,w:d.width||window.innerWidth,h:d.height||window.innerHeight},c=Rt(e),p=c!==void 0&&this.shown?.kind==="native"&&n.capability.animates&&n.capability.layers>=2,m=p?[{property:"opacity",keyframes:[{t:0,v:0},{t:c,v:1}]}]:[],y={slide:e,kind:"native",el:a,mediaId:r,fadesIn:p,ready:Promise.resolve()},A=this.options.fit??"contain";return y.ready=n.prepare({mediaId:r,blob:e.blob.hash,contentType:e.blob.contentType,box:l,z:p?1:0,loop:e.loop,muted:!1,fit:A,...p?{tracks:m}:{}}).then(f=>{f.queued&&(y.queued=!0)}),y}let s=e.url?void 0:URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})),i=K(D("video"));i.dataset.blob=e.blob.hash,i.style.cssText=`${this.fill()};visibility:hidden`,i.muted=!1,i.playsInline=!0,i.preload="auto",i.loop=e.loop,i.src=e.url??s,i.addEventListener("ended",()=>{if(this.shown?.el===i)for(let r of this.endListeners)r(e)}),i.addEventListener("error",()=>{if(this.shown?.el===i)for(let r of this.endListeners)r(e,`video: ${i.error?.message??"error"}`)});let o=new Promise((r,a)=>{if(i.readyState>=2)return r();i.addEventListener("loadeddata",()=>r(),{once:!0}),i.addEventListener("error",()=>a(new Error(`video: ${i.error?.message??"cannot load"}`)),{once:!0})});return this.container.append(i),{slide:e,kind:"video",el:i,objectUrl:s,ready:o}}item(e){this.options.onItem?.(e);for(let n of this.itemListeners)n(e)}releaseHeld(){let e=this.held;this.held=null,e?.length&&setTimeout(()=>{for(let n of e)this.item(n)},0)}fill(){let e=this.options.fit==="stretch"?"fill":this.options.fit??"contain";return Xe.replace("object-fit:contain",`object-fit:${e}`)}dispose(e){if(this.shown!==e){if(e.view?.dispose(),e.el.remove(),e.kind==="video"){let n=e.el;n.pause(),n.removeAttribute("src"),n.load()}e.mediaId&&this.options.media?.release(e.mediaId).catch(()=>{}),e.objectUrl&&URL.revokeObjectURL(e.objectUrl)}}};function Rt(t){if(t.transition?.type==="crossfade")return Math.max(0,Math.min(t.transition.durationMs??1e3,t.durationMs/2))}var Rs=t=>new Promise(e=>setTimeout(e,t));function Nt(t,e,n,s){return new Promise(i=>{let o=Date.now(),r=!1,a=()=>{r||(r=!0,t.style.opacity=n===1?"":String(n),i())};t.style.opacity=String(e);let d=()=>{if(r)return;let l=Math.min(1,(Date.now()-o)/s);if(l>=1)return a();t.style.opacity=String(e+(n-e)*l),requestAnimationFrame(d)};requestAnimationFrame(d),setTimeout(a,s+250)})}function Ge(t){t.play().catch(()=>(t.muted=!0,t.play())).catch(()=>{})}function H(t){return new Promise((e,n)=>{let s=!1,i=()=>{if(!s){s=!0;try{t(),e()}catch(o){n(o)}}};requestAnimationFrame(i),setTimeout(i,250)})}var re={apiVersion:2,properties:[{name:"device",description:"What the device is: id, type, shell and versions, model, resolution and orientation. Fixed while the player runs."},{name:"mode",description:"Where player-core runs: player, preview (in the CMS) or designer. In preview and the designer, commands change nothing."},{name:"timeZone",description:"The player's time zone (IANA), from the host.",event:"time.changed"},{name:"locale",description:"The player's locale (BCP 47), from the host."},{name:"connection",description:"Whether the player is connected to Wyndlass.",event:"connection.changed"},{name:"state",description:"starting, playing, paused, stopped (the standby screen), idle (nothing to play) or error.",event:"state.changed"},{name:"held",description:"A remote pause or stop in effect, by whom and since when; absent when none.",event:"state.changed"},{name:"content",description:"The manifest playing, when it was issued and switched to; absent when there is none.",event:"content.updated"},{name:"now",description:"What each zone shows: the item, its blob and content type, when it started, and its duration and position when known.",event:"item.start"},{name:"audio",description:"The player's volume (0 to 100) and whether it is muted; only when the host can set them.",event:"audio.changed"},{name:"display",description:"The display's power and brightness; only when the host can report them.",event:"display.changed"},{name:"capabilities",description:"What the host can do: audio, display power, display brightness."}],events:[{name:"state.changed",description:"The player's state changed (playing, paused, stopped, idle, error), and who changed it."},{name:"content.updated",description:"A new manifest was switched to."},{name:"item.start",description:"An item started in a zone (source start)."},{name:"item.end",description:"An item ended in a zone (source end): completed, skipped, stopped, error, replaced by new content, or finished by its app."},{name:"item.error",description:"An item could not be shown."},{name:"connection.changed",description:"The player connected to Wyndlass or lost its connection."},{name:"audio.changed",description:"The volume or mute changed, and who changed it."},{name:"display.changed",description:"The display's power or brightness changed, and who changed it."},{name:"time.changed",description:"The clock was adjusted or the time zone changed."},{name:"app.command",description:"A command for apps on the screen (apps.sendCommand); an app reacts if it knows the name."},{name:"app.failed",description:"An app on the screen could not run, or misbehaved (WP-0100): its bundle did not load, it was not an app, it needs a newer player, it kept throwing and was stopped, or it was late or overran a clip."},{name:"app.held",description:"An app version was held back on this screen until the next manifest, after the page stopped answering twice in an hour while it was on screen (WP-0100)."}],commands:[{name:"player.status",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/status",description:"Everything about the player now: device, mode, time zone, locale, connection, state (and any hold), the content playing, what each zone shows, audio, display and what the host can do. Changes nothing.",idempotent:!0},{name:"player.events",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/events",result:"player-api-results.schema.json#/$defs/events",description:"The player's recent events (the last 200), oldest first, after a sequence number: state changes, content updates, item starts and ends, errors, connection, audio, display and time. Pass next as after to poll for more. Changes nothing.",idempotent:!0},{name:"time.now",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/timeNow",description:"The player's current time, its time zone and UTC offset, and whether its clock is synchronized. Changes nothing.",idempotent:!0},{name:"player.play",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Resumes or starts playback on the screen. From a remote caller it also ends a remote pause or stop.",idempotent:!0},{name:"player.pause",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Freezes the screen on what it shows now: the loop's timer and any media stop where they are. From a remote caller it holds until a remote player.play or a restart; apps cannot resume it, and new content arrives paused.",idempotent:!0},{name:"player.stop",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Stops playback and shows the standby screen (black, display on). From a remote caller it holds until a remote player.play or a restart; apps cannot restart it, and new content arrives stopped.",idempotent:!0},{name:"player.next",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/zone",result:"player-api-results.schema.json#/$defs/now",description:"Skips to the next item in a zone (main by default); the current item ends as skipped."},{name:"player.previous",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/zone",result:"player-api-results.schema.json#/$defs/now",description:"Goes back to the previous item in a zone (main by default); the current item ends as skipped."},{name:"player.playItem",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/playItem",result:"player-api-results.schema.json#/$defs/now",description:"Shows a given item of the content playing now in a zone (main by default), then continues the loop from it. Refused for an item the playing manifest does not have."},{name:"audio.setVolume",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setVolume",result:"player-api-results.schema.json#/$defs/audio",description:"Sets the player's volume, 0 to 100. Only on players whose host can set it to a level.",needs:"audio.volume",idempotent:!0},{name:"audio.setMuted",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setMuted",result:"player-api-results.schema.json#/$defs/audio",description:"Mutes or unmutes the player. Only on players whose host can.",needs:"audio",idempotent:!0},{name:"display.setPower",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setPower",result:"player-api-results.schema.json#/$defs/display",description:"Turns the display on or to standby. Only on players whose host can. Standby blanks the screen until it is turned on.",needs:"display.power",idempotent:!0},{name:"display.setBrightness",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setBrightness",result:"player-api-results.schema.json#/$defs/display",description:"Sets the display's brightness, 0 to 100. Only on players whose host can.",needs:"display.brightness",idempotent:!0},{name:"apps.sendCommand",access:"control",apps:"none",remote:!0,params:"player-api-params.schema.json#/$defs/sendCommand",result:"player-api-results.schema.json#/$defs/delivered",description:"Sends a named command, with an optional argument, to the apps on the screen; an app that knows the name reacts. Returns how many apps listen."},{name:"app.finish",access:"control",apps:"read",remote:!1,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/none",description:"An app ends its own slot now: its item ends as finished and the loop moves on. Apps only; not a tool."}],host:[{name:"host.describe",kind:"request",description:"What the device is, its time zone, locale, connection and capabilities.",result:"player-api-host.schema.json#/$defs/describeResult"},{name:"host.time",kind:"request",description:"The host's time and time zone.",result:"player-api-common.schema.json#/$defs/timeNow"},{name:"host.getAudio",kind:"request",description:"The player's volume and mute.",result:"player-api-common.schema.json#/$defs/audio"},{name:"host.setAudio",kind:"request",description:"Sets the volume, the mute or both; answers the audio after.",result:"player-api-common.schema.json#/$defs/audio"},{name:"host.getDisplay",kind:"request",description:"The display's power and brightness.",result:"player-api-common.schema.json#/$defs/display"},{name:"host.setDisplay",kind:"request",description:"Sets the display's power, brightness or both; answers the display after.",result:"player-api-common.schema.json#/$defs/display"},{name:"host.adRequest",kind:"request",description:"Asks Wyndlass's ad broker for one slot of a break over the host's NATS connection (WP-0075); the result is the broker's ad-decision (ad-decision.schema.json). Only a host whose describe says ads is asked."},{name:"host.mediaPrepare",kind:"request",description:"Lays a video out behind the page and prepares it to its first frame; host.mediaState says when (ADR 0055)."},{name:"host.mediaShow",kind:"request",description:"Shows and hides prepared videos; replies once its layer has changed."},{name:"host.mediaControl",kind:"request",description:"Plays, pauses or seeks a video, or asks for its position."},{name:"host.mediaRelease",kind:"request",description:"Removes a video and frees its decoder."},{name:"host.reply",kind:"reply",description:"The host's answer to a request: result or error (unsupported, failed)."},{name:"host.connection",kind:"event",description:"The connection to Wyndlass changed."},{name:"host.timeChanged",kind:"event",description:"The clock was adjusted or the time zone changed."},{name:"host.audioChanged",kind:"event",description:"The volume or mute changed outside the player."},{name:"host.displayChanged",kind:"event",description:"The display changed outside the player."},{name:"host.mediaState",kind:"event",description:"A video was prepared, started, ended or failed, or its position was asked for."},{name:"host.presence",kind:"event",description:"What one of the host's sensors sees now: the presences in view, with random ids that never leave the page (WP-0105)."},{name:"host.sensing",kind:"event",description:"The site's sensing mode as the host applies it, and its sensors (WP-0105)."},{name:"host.serialPorts",kind:"event",description:"The host's sensor controller ports, and which are open (WP-0043)."},{name:"host.serialLine",kind:"event",description:"One line from a sensor controller, as it arrived (WP-0043)."},{name:"host.serialWrite",kind:"request",description:"Writes lines to a sensor controller, a gap apart; replies once written (WP-0043)."},{name:"host.bleWatch",kind:"request",description:"What the page listens for: beacons' iBeacon UUIDs, Eddystone namespaces and addresses; replies once the scan follows it. Sent only to a host that has sent host.bleState (WP-0044)."},{name:"host.bleAdverts",kind:"event",description:"Beacon frames that match the watch, at most four a second of each kind from each beacon (WP-0044)."},{name:"host.bleState",kind:"event",description:"The host's Bluetooth scanner: scanning, idle, off, refused, missing or failed; sent only by a host that has one (WP-0044)."},{name:"host.gpioPins",kind:"event",description:"The GPIO pins the host reads, with their values (WP-0044)."},{name:"host.gpioEdge",kind:"event",description:"A GPIO pin changed (WP-0044)."},{name:"host.mcp",kind:"relay",description:"An MCP message from ctl.dev.{deviceId}, for player-core to answer."},{name:"core.ready",kind:"core",description:"player-core is ready to answer host.mcp; until then the shell answers -32002 itself."},{name:"core.mcpReply",kind:"core",description:"player-core's answer to a host.mcp, for the shell to send back."},{name:"core.sensors",kind:"core",description:"The page's sensor controllers, channels and inputs, and the beacons it hears, whenever they change, for the heartbeat and sensors.recent (WP-0043, WP-0044)."}]};var Ot=Symbol.for("wyndlass.app.attach");var F=class extends Error{constructor(n,s){super(`the app could not run (${n})`);h(this,"reason",n);h(this,"error",s)}};function _(t){try{let e=t instanceof Error?t.name:void 0;return typeof e=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(e)?e:void 0}catch{return}}function oe(t){let e=t?.id;if(typeof e!="string"||e==="")return!0;for(let n=0;n<$t.length;n++)if(e[n]!==$t[n])return!1;return!0}var $t="com.wyndlass.";function Os(t=typeof navigator>"u"?"":navigator.userAgent){let e=/Chrom(?:e|ium)\/(\d+)/.exec(t);return e?Number(e[1]):void 0}function Dt(t,e=re.apiVersion,n=Os()){return!(t?.playerApi!==void 0&&t.playerApi>e||t?.webView!==void 0&&n!==void 0&&t.webView>n)}function $s(t){let e;try{e=t.paging?.()}catch{return null}let n=e;return n&&Number.isInteger(n.count)&&n.count>1&&typeof n.ms=="number"&&n.ms>0?{count:n.count,ms:n.ms}:null}var Qe=new Map;function Ds(){let t=kt(8),e="";for(let n=0;n<8;n++)e+=js[t[n]%26];return e}var js="abcdefghijklmnopqrstuvwxyz",Fs=0;function jt(t,e,n){let s=Qe.get(t);if(!s){let i=je.make([e],"text/javascript");s=import(i).then(o=>Hs(o,n),o=>{throw o instanceof F?o:new F("load",_(o))}).finally(()=>je.revoke(i)),Qe.set(t,s),s.catch(()=>Qe.delete(t))}return s}function Hs(t,e){let n=t.default;if(typeof n=="function"&&n.prototype instanceof HTMLElement){let s=n.prototype[Ot];if(typeof s!="function")throw new F("shape");let i=class extends n{},o=`${e?.element??"wl-app"}-${Ds()}${++Fs}`;try{He.define(o,i)}catch(r){throw new F("shape",_(r))}return{tag:o,shape:"class",attach:(r,a)=>{vt(s,r,[a])}}}if(oe(e)&&typeof t.element=="string"&&He.get(t.element))return{tag:t.element,shape:"legacy"};throw new F("shape")}var Bs=3,Us=6e4,Ae=class{constructor(e,n){h(this,"element",e);h(this,"o",n);h(this,"faults",[]);h(this,"halted",!1)}get stopped(){return this.halted}has(e){try{return!this.halted&&typeof this.element[e]=="function"}catch{return!1}}update(e){this.halted||this.run(()=>this.element.update?.(e))}reset(){this.halted||this.run(()=>this.element.reset?.())}paging(){return this.halted?null:$s(this.element)}hook(e,n){if(this.halted)return Promise.resolve();let s;try{s=this.element[e]?.(n)}catch(i){return this.fault(i),Promise.resolve()}return Promise.resolve(s).then(()=>{},i=>this.fault(i))}stop(e){if(!this.halted){this.halted=!0;try{this.element.remove()}catch{}this.o.dispose?.(),this.o.onStop(e)}}dispose(){this.o.dispose?.()}run(e){try{e()}catch(n){this.fault(n)}}fault(e){let n=(this.o.now??Date.now)();this.faults=this.faults.filter(s=>n-s<Us),this.faults.push(n),this.faults.length>=Bs&&this.stop(_(e))}},qs=24*3600*1e3,Me=class t{constructor(e){h(this,"o",e);h(this,"memory",new Map);h(this,"inflight",new Map);h(this,"images",new Map);h(this,"pictures",new Map)}now(){return this.o.now?.()??Date.now()}url(e){let n=this.o.base();if(!n)return;let s=this.o.token(),i=`${n.replace(/\/$/,"")}${e}`;return s?`${i}${i.includes("?")?"&":"?"}${s}`:i}stored(e){return this.memory.get(e)??this.o.store?.getItem(e)??void 0}static keyOf(e,n){let s=Object.keys(n).sort().map(i=>`${encodeURIComponent(i)}=${encodeURIComponent(String(n[i]))}`).join("&");return{query:s,key:`wyndlass:data:${e}?${s}`}}kept(e,n){let s=this.stored(t.keyOf(e,n).key);if(s)try{let i=JSON.parse(s);return this.dress(i,Date.parse(i.expiresAt)<=this.now())}catch{return}}keep(e,n){this.memory.set(e,n);try{this.o.store?.setItem(e,n)}catch{}}get(e,n){let{query:s,key:i}=t.keyOf(e,n),o=this.inflight.get(i);if(o)return o;let r=this.o.fetch??fetch,a=(async()=>{let d=this.stored(i),l=d?JSON.parse(d):void 0;if(l&&Date.parse(l.expiresAt)>this.now())return this.dress(l,!1);let c=this.url(`/v0/${encodeURIComponent(e)}?${s}`);try{if(!c)throw new Error("no data service");let p=await r(c);if(!p.ok)throw new Error(`the data service answered ${p.status}`);let m=await p.text();return this.keep(i,m),this.dress(JSON.parse(m),!1)}catch(p){if(l&&Date.parse(l.expiresAt)+qs>this.now())return this.dress(l,!0);throw p}})().finally(()=>this.inflight.delete(i));return this.inflight.set(i,a),a}dress(e,n){return{...e,offline:n,ageMs:Math.max(0,this.now()-Date.parse(e.fetchedAt))}}image(e){let n=this.images.get(e);return n||(n=this.imageBlob(e).then(s=>URL.createObjectURL(s)),this.images.set(e,n),n.catch(()=>this.images.delete(e))),n}imageBlob(e){let n=this.pictures.get(e);if(!n){let s=this.url(e);n=s?(this.o.fetch??fetch)(s).then(i=>{if(!i.ok)throw new Error(`the image answered ${i.status}`);return i.blob()}):Promise.reject(new Error("no data service")),this.pictures.set(e,n),n.catch(()=>this.pictures.delete(e))}return n}};var Ft="wyndlass.frame";function Ut(t,e){return!!t&&(t.apps?t.apps(e):!oe(e))}var _s=new Set(["load","shape","unsupported","error","late","overran"]),Ht=50,Bt=250,et=class{constructor(e,n,s,i){h(this,"frame",e);h(this,"port",n);h(this,"o",i);h(this,"halted",!1);h(this,"pages");h(this,"hooks");h(this,"pending",new Map);h(this,"next",0);h(this,"told");h(this,"last");h(this,"quiet");this.hooks=new Set(s.hooks),this.pages=s.paging}get stopped(){return this.halted}has(e){return!this.halted&&this.hooks.has(e)}paging(){return this.halted?null:this.pages}update(e){if(this.halted)return;let n=performance.now(),s=this.last;this.last={t:e,at:n};let i=this.told;if(i&&(i.running?Math.abs(e-(i.t+(n-i.at)))<=Ht:e===i.t)){i.running&&this.watch();return}let o=!!s&&n-s.at<Bt&&e>s.t&&Math.abs(e-s.t-(n-s.at))<=Ht;this.told={t:e,at:n,running:o},this.post({type:"clock",t:e,running:o}),o&&this.watch()}reset(){this.halted||this.post({type:"reset"})}hook(e,n){if(this.halted)return Promise.resolve();let s=++this.next;return new Promise(i=>{this.pending.set(s,i),this.post({type:"hook",id:s,kind:e,options:n})})}stop(e){this.halted||(this.halted=!0,this.frame.remove(),this.dispose(),this.o.onStop(e))}dispose(){clearTimeout(this.quiet),this.port.close();for(let e of this.pending.values())e();this.pending.clear()}heard(e){e.type==="paging"?this.pages=_t(e.paging):e.type==="hooked"&&typeof e.id=="number"?(this.pending.get(e.id)?.(),this.pending.delete(e.id)):e.type==="stopped"&&this.stop(Wt(e.error))}post(e){try{this.port.postMessage(e)}catch{}}watch(){clearTimeout(this.quiet),this.quiet=setTimeout(()=>{!this.told?.running||!this.last||this.halted||(this.told={...this.last,running:!1},this.post({type:"clock",t:this.last.t,running:!1}))},Bt)}};async function qt(t,e,n,s,i,o,r,a,d,l){let c=t.appInfo??n.info,p;try{p=new URL(d.frames).origin}catch{return l("unsupported")}let m=document.createElement("iframe");m.setAttribute("sandbox","allow-scripts"),m.setAttribute("referrerpolicy","no-referrer"),m.title=c?.id??"app",m.tabIndex=-1,m.dataset.frame=c?.id??"",m.style.cssText="display:block;width:100%;height:100%;border:0;pointer-events:none;background:transparent;color-scheme:normal";let y=new Promise(M=>{m.addEventListener("load",()=>M(!0),{once:!0}),setTimeout(()=>M(!1),o)});m.src=`${p}/`,e.append(m);let A=(M,w)=>(m.remove(),l(M,w));if(!await y||!m.contentWindow)return A("load");let{port1:f,port2:k}=new MessageChannel,b={type:"init",element:{id:t.id,type:"app",app:t.app,props:t.props??{},...t.binding?{binding:t.binding}:{},...t.appInfo?{appInfo:t.appInfo}:{}},bundle:{hash:n.hash,bytes:n.bytes.slice(),...n.info?{info:n.info}:{}},...s.place?{place:s.place}:{},screen:s.screen??null,timeZone:s.timeZone,locale:s.locale,preview:!!s.preview,clock:{t:i.now(),running:!1,startedAt:i.startedAt()},fonts:Vs(t.props??{},d),snapshots:Ks(t,s),deadlineMs:o};return new Promise(M=>{let w,T=!1,N=G=>{T||(T=!0,clearTimeout(j),M(G))},j=setTimeout(()=>{w?(N({app:w,late:!0}),w.update(i.now())):(f.close(),N(A("load")))},o+1e3),U=!1;f.onmessage=G=>{let x=G.data;if(!(!x||typeof x!="object")){if(x.type==="ask"){U&&Ws(x,s,f);return}if(w){x.type==="ready"?(N({app:w,late:x.late===!0}),w.update(i.now())):w.heard(x);return}x.type==="hello"&&!U?(U=!0,f.postMessage(b,[b.bundle.bytes.buffer])):x.type==="made"&&Array.isArray(x.hooks)?(w=new et(m,f,{type:"made",hooks:x.hooks.filter(C=>C==="enter"||C==="exit"),paging:_t(x.paging)},{onStop:a}),r(w)):x.type==="failed"&&(f.close(),N(A(_s.has(x.reason)?x.reason:"error",Wt(x.error))))}},m.contentWindow.postMessage({type:Ft},"*",[k])})}async function Ws(t,e,n){if(typeof t.id!="number")return;let s=(i,o=[])=>{try{n.postMessage(i,o)}catch{}};try{if(!e.data)throw new Error("no data service");if(t.what==="data"&&typeof t.source=="string")s({type:"answer",id:t.id,value:await e.data.get(t.source,zs(t.params))});else if(t.what==="image"&&typeof t.path=="string"){let i=await e.data.imageBlob(t.path),o=new Uint8Array(await i.arrayBuffer());s({type:"answer",id:t.id,value:{bytes:o,type:i.type}},[o.buffer])}else throw new TypeError("not a question")}catch(i){s({type:"answer",id:t.id,error:_(i)??"Error"})}}function zs(t){let e={};if(t&&typeof t=="object")for(let[n,s]of Object.entries(t))(typeof s=="string"||typeof s=="number"||typeof s=="boolean")&&(e[n]=s);return e}function _t(t){let e=t;return e&&Number.isInteger(e.count)&&e.count>1&&typeof e.ms=="number"&&e.ms>0?{count:e.count,ms:e.ms}:null}function Wt(t){return typeof t=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(t)?t:void 0}function Vs(t,e){let n=new Set;for(let i of Object.values(t)){let o=i?.family;i&&typeof i=="object"&&typeof o=="string"&&n.add(o)}let s=[];for(let i of e.fonts??[]){let o=n.has(i.family)?e.font?.(i.blob):void 0;o&&s.push({family:i.family,weight:i.weight,style:i.style,blob:i.blob,bytes:o.slice()})}return s}function Ks(t,e){let n=new Set;t.binding?.table&&n.add(t.binding.table);for(let i of Object.values(t.props??{}))typeof i=="string"&&/^tbl_[0-9a-z]{26}$/.test(i)&&n.add(i);let s={};for(let i of n){let o=e.snapshot?.(i);o&&(s[i]=o)}return s}function zt(t){let e=te?.setStyle??((n,s,i,o)=>n.style.setProperty(s,i,o));e(t,"contain","strict","important"),e(t,"overflow","clip","important")}var Vt=t=>!!(t?.filter?.length||t?.sort||t?.limit),Zs=t=>t==null||t==="";function Js(t,e){if(!Zs(t))switch(e){case"number":case"currency":{let n=Number(t);return Number.isFinite(n)?n:void 0}case"datetime":{let n=Date.parse(String(t));return Number.isNaN(n)?void 0:n}case"boolean":return t===!0||String(t).toLowerCase()==="true"?1:0;case"date":return String(t).slice(0,10);default:return String(t).toLowerCase()}}function Ys(t,e){let n=Array.from(t),s=Array.from(e);for(let i=0;i<n.length&&i<s.length;i++){let o=n[i].codePointAt(0)-s[i].codePointAt(0);if(o!==0)return o}return n.length-s.length}function Xs(t,e,n,s,i){let o=t,r=Array.isArray(o?.rows)?o.rows:[],a=Array.isArray(o?.columns)?o.columns:void 0,d=l=>a?.find(c=>c.key===l)?.type??"text";for(let l of e.filter??[]){if(a&&!a.some(m=>m.key===l.column))return[];let c=l.value;if(c!==null&&typeof c=="object"){let m=c;if(c=ke(n,s,m.screen,m.key),c===void 0)return[]}let p=d(l.column);r=r.filter(m=>Pt(m[l.column],l.op,c,p,i,s))}if(e.sort){let{column:l}=e.sort,c=d(l),p=e.sort.order==="desc"?-1:1;r=r.map((m,y)=>({row:m,i:y,key:Js(m[l],c)})).sort((m,y)=>{if(m.key===void 0||y.key===void 0)return m.key===void 0&&y.key===void 0?m.i-y.i:m.key===void 0?1:-1;let A=typeof m.key=="number"&&typeof y.key=="number"?m.key-y.key:Ys(String(m.key),String(y.key));return A===0?m.i-y.i:A<0?-p:p}).map(m=>m.row)}return e.limit?r.slice(0,e.limit):r}function Kt(t,e,n,s,i){return!t||typeof t!="object"?t:{...t,rows:Xs(t,e,n,s,i)}}function Gs(t){if(!t)return"#000000";if("value"in t)return t.value;let e=t.gradient,n=[...e.stops].sort((s,i)=>s.at-i.at).map(s=>`${s.color.value} ${Math.round(s.at*1e4)/100}%`).join(", ");return e.kind==="radial"?`radial-gradient(circle, ${n})`:`linear-gradient(${e.angle??180}deg, ${n})`}function Jt(t,e,n,s=[]){let i=D("div");if(i.dataset.background="",i.style.cssText="position:absolute;inset:0;pointer-events:none",i.style.background=Gs(e),s.length){let o=[`M0 0H${n.width}V${n.height}H0Z`,...s.map(d=>`M${d.x} ${d.y}H${d.x+d.w}V${d.y+d.h}H${d.x}Z`)].join(""),r=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n.width} ${n.height}" preserveAspectRatio="none"><path fill-rule="evenodd" d="${o}"/></svg>`,a=`url("data:image/svg+xml,${encodeURIComponent(r)}")`;i.dataset.holes=String(s.length);for(let[d,l]of[["maskImage",a],["maskSize","100% 100%"],["maskRepeat","no-repeat"]])i.style[d]=l,i.style[`webkit${d[0].toUpperCase()}${d.slice(1)}`]=l}return t.prepend(i),i}function Yt(t,e=t.frame){let n=D("div");return n.dataset.element=t.id,n.style.cssText="position:absolute;box-sizing:border-box",Qs(n,e),n}function Qs(t,e){Object.assign(t.style,{left:`${e.x}px`,top:`${e.y}px`,width:`${e.w}px`,height:`${e.h}px`,transform:e.rotation?`rotate(${e.rotation}deg)`:""})}function Xt(t,e,n){if(e.type==="text"){let s=e.style??{},i=Gt(n);t.textContent=e.bind?ze(e.bind,i):Ve(e.text??"",i),Object.assign(t.style,{fontFamily:s.font?.family?`"${s.font.family}", sans-serif`:"",fontWeight:String(s.font?.weight??400),fontStyle:s.font?.style??"normal",fontSize:`${s.size??48}px`,color:s.color?.value??"#ffffff",background:s.background?.value??"",whiteSpace:"pre-wrap",textAlign:{start:"left",end:"right"}[s.align??"start"]??s.align??"left",display:"flex",flexDirection:"column",justifyContent:{top:"flex-start",middle:"center",bottom:"flex-end"}[s.verticalAlign??"top"],lineHeight:s.lineHeight?String(s.lineHeight):"",opacity:s.opacity!==void 0?String(s.opacity):"",textDecorationLine:s.decoration?.length?s.decoration.join(" "):"",textTransform:s.transform&&s.transform!=="none"?s.transform:"",letterSpacing:s.letterSpacing?`${s.letterSpacing}em`:"",textShadow:s.shadow?`${s.shadow.x??0}px ${s.shadow.y??2}px ${s.shadow.blur??4}px ${s.shadow.color?.value??"#000000"}`:""})}else e.type==="shape"&&ei(e)?(t.style.opacity=e.opacity!==void 0?String(e.opacity):"",t.append(ti(e))):e.type==="shape"?Object.assign(t.style,{background:e.fill?.value??"",opacity:e.opacity!==void 0?String(e.opacity):"",border:e.stroke?.width?`${e.stroke.width}px ${e.stroke.dash??"solid"} ${e.stroke.color?.value??"#000"}`:"",borderRadius:e.shape==="ellipse"?"50%":e.radius?`${e.radius}px`:"",overflow:e.clip!==!1?"hidden":""}):e.type==="library"&&(t.style.overflow="hidden",t.style.background=e.playback?.background?.value??"")}function Gt(t){return{screen:t?.screen??null,timeZone:t?.timeZone,tables:new Proxy({},{get:(e,n)=>typeof n=="string"?t?.snapshot?.(n):void 0})}}var ei=t=>t.shape==="line"||t.shape==="arrow"||t.shape==="polygon";function ti(t){let e=ye("svg");e.setAttribute("width","100%"),e.setAttribute("height","100%"),e.style.cssText="position:absolute;inset:0;overflow:visible";let n=t.frame.w,s=t.frame.h,i=t.stroke?.width??(t.shape==="polygon"?0:4),r=(t.points?.length?t.points:t.shape==="polygon"?[[.5,0],[1,1],[0,1]]:[[0,.5],[1,.5]]).map(([c,p])=>[c*n,p*s]),a=t.stroke?.color?.value??"#000000",d=t.stroke?.dash==="dashed"?`${i*3} ${i*2}`:t.stroke?.dash==="dotted"?`0 ${i*2}`:"",l=ye(t.shape==="polygon"?"polygon":"polyline");if(l.setAttribute("points",r.map(c=>`${c[0]},${c[1]}`).join(" ")),l.setAttribute("fill",t.shape==="polygon"?t.fill?.value??"none":"none"),i&&(l.setAttribute("stroke",a),l.setAttribute("stroke-width",String(i)),l.setAttribute("stroke-linecap",t.stroke?.dash==="dotted"?"round":"butt"),l.setAttribute("stroke-linejoin","round"),d&&l.setAttribute("stroke-dasharray",d)),e.append(l),t.shape==="arrow"&&r.length>=2){let[c,p]=[r[r.length-2],r[r.length-1]],m=Math.atan2(p[1]-c[1],p[0]-c[0]),y=Math.max(10,i*3.5),A=k=>`${p[0]-y*Math.cos(m)+k*y*.5*Math.sin(m)},${p[1]-y*Math.sin(m)-k*y*.5*Math.cos(m)}`;l.setAttribute("points",[...r.slice(0,-1),[p[0]-y*.8*Math.cos(m),p[1]-y*.8*Math.sin(m)]].map(k=>`${k[0]},${k[1]}`).join(" "));let f=ye("polygon");f.setAttribute("points",`${p[0]},${p[1]} ${A(1)} ${A(-1)}`),f.setAttribute("fill",a),e.append(f)}return e}async function Qt(t,e,n,s,i,o,r,a=()=>{},d){let l=t.appInfo??n.info;zt(e),l?.id&&(e.dataset.app=l.id);let c=(x,C)=>(e.dataset.appFailed=x,{failed:x,...C?{error:C}:{}});if(!Dt(l?.requires))return c("unsupported");let p=s.appHost;if(p&&!p.custom&&!oe(l))return c("unsupported");if(Ut(d,l))return qt(t,e,n,s,i,o,r,a,d,c);let m=t.app;if(p?.held?.(m))return e.dataset.appHeld="",{held:!0};await p?.mounting?.(m,l?.id);let y=!1,A=()=>{y||(y=!0,p?.unmounted?.(m))},f;try{f=await jt(n.hash,n.bytes,l)}catch(x){return A(),x instanceof F?c(x.reason,x.error):c("load")}let k=f.tag,b=l?.id?s.controllerFor?.({id:l.id,instance:t.id,capability:l.player??"read"}):void 0,M,w=new Promise(x=>{M=x}),T,N={app:l?.id??t.app.id,element:k,props:St(t.props??{},Gt(s)),place:s.place,screen:s.screen??null,timeZone:s.timeZone??Intl.DateTimeFormat().resolvedOptions().timeZone,locale:s.locale??navigator.language,preview:!!s.preview,now:i.now,startedAt:i.startedAt,ready:()=>M(),data:(x,C)=>s.data?s.data.get(x,C):Promise.reject(new Error("no data service")),kept:(x,C)=>s.data?.kept?.(x,C),image:x=>s.data?s.data.image(x):Promise.reject(new Error("no data service")),snapshot:x=>{let C=s.snapshot?.(x)??null;if(!C||t.binding?.table!==x||!Vt(t.binding))return C;let Q=Kt(C,t.binding,s.screen,N.timeZone,i.now());return T?.from===C&&T.rows.length===Q.rows.length&&T.rows.every((Cn,Rn)=>Cn===Q.rows[Rn])?T.snapshot:(T={from:C,rows:Q.rows,snapshot:Q},Q)},...t.binding?{binding:t.binding}:{},...t.sensors?.includes("impressions")&&s.impressions?.measuring?{impressions:{now:()=>s.impressions.now(),on:x=>s.impressions.on(x)}}:{},...b?{controller:b}:{}},j;try{j=D(k),f.shape==="class"?f.attach(j,N):j.wyndlass=N}catch(x){return b?.dispose(),A(),c("error",_(x))}j.style.cssText="display:block;width:100%;height:100%",p?.contained?.(e,j);let U=new Ae(j,{onStop:a,dispose:()=>{b?.dispose(),A()}});e.append(j),r(U);let G=await Promise.race([w.then(()=>!1),new Promise(x=>setTimeout(()=>x(!0),o))]);return U.update(N.now()),{app:U,late:G}}var Zt=new Set;async function en(t,e){for(let n of t){let s=`${n.family}/${n.weight??400}/${n.style??"normal"}/${n.blob}`,i=e(n.blob);if(!(Zt.has(s)||!i||typeof FontFace>"u"))try{let o=new FontFace(n.family,i,{weight:String(n.weight??400),style:n.style??"normal"});document.fonts.add(await o.load()),Zt.add(s)}catch{}}}function ae(t){let e=[],n=null;for(let s of t){let i=s.start,o=Math.max(0,i.kind==="at"?i.ms:i.kind==="withPrevious"?(n?.startMs??0)+(i.offsetMs??0):(n?.endMs??0)+(i.offsetMs??0));n={startMs:o,endMs:o+s.durationMs},e.push(n)}return{clips:e,autoDurationMs:e.reduce((s,i)=>Math.max(s,i.endMs),0)}}var ni=new Set(["x","y","scaleX","scaleY","rotation","opacity","reveal"]);function si(t,{pageBelow:e=!1}={},n=null){let s=o=>({path:"page",reason:o});if(!n)return s("no-media-layer");if(e)return s("page-element-beneath");let i=t.slice(0,-1).filter(o=>o.type==="shape"&&o.clip!==!1);if(i.some(o=>o.shape!=="rectangle"||(o.radius??0)>0))return s("clipped-to-shape");if(i.some(o=>(o.rotation??0)%360!==0))return s("clipped-by-rotated-shape");if(!n.animates){if(t.some(o=>(o.rotation??0)%360!==0))return s("rotated");if(t.some(o=>(o.opacity??1)<1))return s("translucent");if(t.some(o=>(o.animated??[]).some(r=>ni.has(r))))return s("animated")}return{path:"native"}}var ii={fade:["opacity"],slide:["x","opacity"],scale:["scaleX","scaleY","opacity"],pulse:["scaleX","scaleY"],loop:["scaleX","scaleY"],wipe:["reveal"]};function tn(t,e,n,s){let i=e&&t.overrides?.[e]||{},o=new Map;for(let p of t.animation?.clips??[]){let m=[...p.preset?ii[p.preset]??[]:[],...(p.lanes??[]).map(y=>y.property)];o.set(p.element,[...o.get(p.element)??[],...m])}let r=p=>p.type==="library"&&!!p.source&&!!n[p.source.item.id]?.video,a=p=>!r(p)&&!(p.type==="shape"&&!p.fill&&!(p.stroke?.width??0)),d=[],l=(p,m,y,A)=>{for(let f of p){let k=i[f.id];if(k?.hidden??f.hidden??!1)continue;let b=k?.frame??f.frame,M={x:m.x+b.x,y:m.y+b.y,w:b.w,h:b.h},w={type:f.type==="shape"?"shape":"library",rotation:b.rotation??0,opacity:f.opacity??f.style?.opacity??1,animated:o.get(f.id)??[],...f.type==="shape"?{shape:f.shape,radius:f.radius,clip:f.clip}:{}};d.push({e:f,box:M,chain:[...y,w],ancestors:A}),f.type==="shape"&&l(f.children??[],M,[...y,w],[...A,f])}};l(t.elements,{x:0,y:0},[],[]);let c=(p,m)=>p.x<m.x+m.w&&m.x<p.x+p.w&&p.y<m.y+m.h&&m.y<p.y+p.h;return d.flatMap((p,m)=>{if(!r(p.e))return[];let y=d.slice(0,m).some(A=>a(A.e)&&c(A.box,p.box));return[{element:p.e.id,path:si(p.chain,{pageBelow:y},s),box:p.box}]})}var ri=[0,0,1,1];function tt(t,e){if(e<=0)return 0;if(e>=1)return 1;let[n,s,i,o]=t;if(n===s&&i===o)return e;let r=3*n,a=3*(i-n)-r,d=1-r-a,l=3*s,c=3*(o-s)-l,p=1-l-c,m=w=>((d*w+a)*w+r)*w,y=w=>((p*w+c)*w+l)*w,A=w=>(3*d*w+2*a)*w+r,f=1e-7,k=e;for(let w=0;w<8;w++){let T=m(k)-e;if(Math.abs(T)<f)return y(k);let N=A(k);if(Math.abs(N)<1e-6)break;k-=T/N}let b=0,M=1;for(k=e;b<M;){let w=m(k);if(Math.abs(w-e)<f||(e>w?b=k:M=k,k=(M-b)/2+b,M-b<f))break}return y(k)}function Ee(t,e){let n=t.keyframes;if(e<=n[0].t)return n[0].v;let s=n[n.length-1];if(e>=s.t)return s.v;let i=0;for(;e>=n[i+1].t;)i++;let o=n[i],r=n[i+1];return o.v+(r.v-o.v)*tt(o.easing??ri,(e-o.t)/(r.t-o.t))}function nn(t,e,n){let s={x:t.x,y:t.y,scaleX:1,scaleY:1,rotation:0,opacity:1};for(let i of e??[])s[i.property]=Ee(i,n);return s}var de={linear:[0,0,1,1],ease:[.25,.1,.25,1],"ease-in":[.42,0,1,1],"ease-out":[0,0,.58,1],"ease-in-out":[.42,0,.58,1]};function sn(t){if(!t)return de.ease;if(de[t])return de[t];let n=/^cubic-bezier\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)$/.exec(t)?.slice(1).map(Number);return n&&n.every(Number.isFinite)?n:de.ease}function oi(t){let e=typeof t=="string"?/^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})?$/.exec(t):null;if(!e)return null;let n=parseInt(e[1],16);return[n>>16&255,n>>8&255,n&255,e[2]?parseInt(e[2],16)/255:1]}var an={fade:()=>({opacity:0}),slide:t=>({x:t.x-t.w*.1,opacity:0}),scale:()=>({scaleX:.8,scaleY:.8,opacity:0}),wipe:()=>({reveal:0}),typewriter:()=>({chars:0}),"count-up":()=>({count:0})},zo=Object.keys(an);var ai={x:t=>t.x,y:t=>t.y,scaleX:()=>1,scaleY:()=>1,rotation:()=>0,opacity:()=>1,reveal:()=>1,chars:()=>1,count:()=>1},rn=1e3,di=de["ease-in-out"];function dn(t,e){let n=ae(t).clips,s=new Map,i=new Set,o=a=>{let d=s.get(a);return d||s.set(a,d={keys:new Map,colour:null,loops:[],tickers:[]}),d},r=(a,d,l)=>{let c=o(a).keys,p=c.get(d)??[];c.set(d,p),p.push(l)};t.forEach((a,d)=>{let l=e.get(a.element);if(!l)return;let{startMs:c,endMs:p}=n[d],m=sn(a.easing);if(a.lanes?.length){for(let f of a.lanes)for(let k of f.keyframes){let b={t:c+k.at,easing:sn(k.easing??a.easing)};if(f.property==="color"){let M=oi(k.value);if(!M)continue;let w=o(a.element);w.colour??(w.colour=[[],[],[],[]]),M.forEach((T,N)=>w.colour[N].push({...b,v:T}))}else r(a.element,f.property,{...b,v:Number(k.value)})}return}let y=a.preset??"fade";if(a.kind==="emphasis"){y==="pulse"?(r(a.element,"pulse",{t:c,v:1,easing:m}),r(a.element,"pulse",{t:(c+p)/2,v:1.05,easing:m}),r(a.element,"pulse",{t:p,v:1})):y==="loop"?o(a.element).loops.push({startMs:c,endMs:p}):y==="ticker"?o(a.element).tickers.push({startMs:c,endMs:p}):i.add(y);return}let A=an[y];if(!A){i.add(y);return}for(let[f,k]of Object.entries(A(l))){let b=ai[f](l),[M,w]=a.kind==="enter"?[k,b]:[b,k];r(a.element,f,{t:c,v:M,easing:m}),r(a.element,f,{t:p,v:w})}});for(let a of s.values()){for(let d of a.keys.values())d.sort((l,c)=>l.t-c.t);for(let d of a.colour??[])d.sort((l,c)=>l.t-c.t)}return{motions:s,unplayed:[...i].sort()}}var li=["x","y","scaleX","scaleY","rotation","opacity"];function ln(t,e,n,s=!1){if(s)return pi(t,e,n);let i=(d,l)=>{let c=t?.keys.get(d);return c?Ee({property:"x",keyframes:c},n):l},o=nn(e,li.flatMap(d=>t?.keys.get(d)?[{property:d,keyframes:t.keys.get(d)}]:[]),n),r=i("pulse",1);for(let d of t?.loops??[])if(n>=d.startMs&&n<d.endMs){let l=(n-d.startMs)%rn/rn;r*=1+.05*tt(di,l<.5?l*2:2-l*2)}let a=t?.tickers.find(d=>n>=d.startMs&&n<d.endMs);return{...o,reveal:i("reveal",1),chars:i("chars",1),count:i("count",1),pulse:r,scroll:a?(n-a.startMs)/(a.endMs-a.startMs):null,color:t?.colour?t.colour.map(d=>Ee({property:"x",keyframes:d},n)):null}}function on(t,e){if(e<t[0].t)return t[0].v;let n=0;for(;n+1<t.length&&t[n+1].t<=e;)n++;return n+1<t.length?t[n+1].v:t[n].v}function pi(t,e,n){let s=(i,o)=>{let r=t?.keys.get(i);return r?on(r,n):o};return{x:s("x",e.x),y:s("y",e.y),scaleX:s("scaleX",1),scaleY:s("scaleY",1),rotation:s("rotation",0),opacity:s("opacity",1),reveal:s("reveal",1),chars:s("chars",1),count:s("count",1),pulse:1,scroll:null,color:t?.colour?t.colour.map(i=>on(i,n)):null}}function ci(t,e){let n=Array.from(t);return e>=1?t:n.slice(0,Math.max(0,Math.round(e*n.length))).join("")}function ui(t,e){return e>=1?t:t.replace(/\d+(?:\.\d+)?/g,n=>(Number(n)*Math.max(0,e)).toFixed((n.split(".")[1]??"").length))}function pn(t,e,n){if(t<=1||e<=0)return[{page:0,startMs:0,endMs:n}];let s=[];for(let i=0;i*e<n&&i<1e3;i++)s.push({page:i%t,startMs:i*e,endMs:Math.min((i+1)*e,n)});return s}function cn(t,e,n,s){t.style.left=`${n.x}px`,t.style.top=`${n.y}px`,t.style.transform=`rotate(${(e.frame.rotation??0)+n.rotation}deg) scale(${n.scaleX*n.pulse}, ${n.scaleY*n.pulse})`,t.style.opacity=String((e.opacity??e.style?.opacity??1)*n.opacity),t.style.clipPath=n.reveal>=1?"":`inset(0 ${(1-Math.max(0,n.reveal))*100}% 0 0)`;let i=n.color?`rgba(${n.color.slice(0,3).map(o=>Math.round(o)).join(",")},${n.color[3]})`:null;if(e.type==="text"&&s!==void 0){let o=ui(ci(s,n.chars),n.count);if(n.scroll!==null){let r=t.querySelector("[data-ticker]");r||(r=document.createElement("span"),r.dataset.ticker="",r.style.cssText="display:inline-block;white-space:nowrap;will-change:transform",t.replaceChildren(r),t.style.overflow="hidden"),r.textContent!==o&&(r.textContent=o);let a=e.frame.w,d=r.scrollWidth;r.style.transform=`translateX(${a-n.scroll*(a+d)}px)`}else(t.textContent!==o||t.querySelector("[data-ticker]"))&&(t.textContent=o,t.style.overflow="");i&&(t.style.color=i)}else if(e.type==="shape"&&i){let o=t.querySelector("polygon, polyline");o&&e.shape==="polygon"?o.setAttribute("fill",i):o||(t.style.background=i)}}function hi(t){let e=t>>>0;return()=>{e=e+1831565813|0;let n=Math.imul(e^e>>>15,1|e);return n=n+Math.imul(n^n>>>7,61|n)^n,((n^n>>>14)>>>0)/4294967296}}async function un(t,e,n,s){let i=parseInt((await We(_e(`${t}
|
|
3
|
+
${e}
|
|
4
|
+
${n}`))).slice(0,8),16),o=hi(i),r=Array.from({length:s},(a,d)=>d);for(let a=s-1;a>0;a--){let d=Math.floor(o()*(a+1));[r[a],r[d]]=[r[d],r[a]]}return r}var mi=250,fi=1e3;function Ct(t,e){let n=t.animation?.duration;if(n?.kind==="fixed")return{durationMs:n.ms};if(n?.kind==="untilPlaylistEnds"){let s=fn(t.elements,n.element);return{durationMs:(s?.source?.kind==="playlist"?e.playlist(s.source.item)??[]:s?.source?[{item:s.source.item}]:[]).reduce((r,a)=>r+(e.slide(a,{imageDurationMs:s?.playback?.imageDurationMs})?.durationMs??0),0)||1e4,untilPass:n.element}}return{durationMs:ae(t.animation?.clips??[]).autoDurationMs}}function mn(t,e,n=0){let s={},i=o=>{for(let r of o){if(r.type==="library"&&r.source){let a=r.source.kind==="playlist"?e.playlist(r.source.item)??[]:[{item:r.source.item}];s[r.source.item.id]={video:a.some(d=>{let l=e.slide(d);if(!l)return!1;if(l.blob.contentType.startsWith("video/"))return!0;let c=l.layout;return!!c&&n<4&&Object.values(mn(c.doc,c.source,n+1)).some(p=>p.video)})}}r.children&&i(r.children)}};return i(t.elements),s}function fn(t,e){for(let n of t){if(n.id===e)return n;let s=n.children&&fn(n.children,e);if(s)return s}}var J=class{constructor(e,n,s,i={}){h(this,"source",n);h(this,"o",i);h(this,"el");h(this,"ready");h(this,"unplayed");h(this,"doc");h(this,"motions");h(this,"native");h(this,"animated",[]);h(this,"loops",[]);h(this,"startedAt",0);h(this,"frame",0);h(this,"running",!1);h(this,"disposed",!1);h(this,"apps",[]);h(this,"heldAt");h(this,"late",[]);h(this,"overran",[]);h(this,"timers",[]);h(this,"reduced",typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches);let o=s.clientWidth||window.innerWidth,r=s.clientHeight||window.innerHeight,a=!e.profiles[0].width,d=a?{...e.profiles[0],width:o,height:r}:e.profiles[0];a&&(e={...e,elements:e.elements.map(b=>b.frame.w?b:{...b,frame:{x:0,y:0,w:o,h:r}})});let l=Math.min(o/d.width,r/d.height);this.el=D("div"),this.el.dataset.layout=d.id,this.el.style.cssText=`position:absolute;left:${(o-d.width*l)/2}px;top:${(r-d.height*l)/2}px;width:${d.width}px;height:${d.height}px;transform:scale(${l});transform-origin:0 0;overflow:hidden`,s.append(this.el),this.doc=e;let c=new Map,p=b=>{for(let M of b)c.set(M.id,M.frame),M.children&&p(M.children)};p(e.elements);let m=dn(e.animation?.clips??[],c);this.motions=m.motions;let y=new Set(m.unplayed.map(b=>`preset ${b}`)),A=[this.loadFonts()],f=i.media?tn(e,void 0,mn(e,n),i.media.capability):[];this.native=new Set(f.filter(b=>b.path.path==="native").map(b=>b.element));let k=(b,M)=>{for(let w of b){if(w.hidden)continue;let T=this.element(w,y,A);M.append(T),w.children?.length&&w.type==="shape"&&k(w.children,T)}};k(e.elements,this.el),e.background&&Jt(this.el,e.background,d,f.filter(b=>this.native.has(b.element)).map(b=>b.box)),this.unplayed=[...y].sort(),this.ready=Promise.all(A).then(()=>{})}path(e){return this.o.path?`${this.o.path}/${e}`:e}element(e,n,s){let i=Yt(e);Xt(i,e,this.source);let o=this.motions.get(e.id);if(o){let r={el:i,e,motion:o,text:e.type==="text"?i.textContent??"":void 0};this.animated.push(r),this.place(r,0)}if(e.type==="library"&&e.source){let r=e.source.kind==="playlist"?this.source.playlist(e.source.item)??[]:[{item:e.source.item}],a=new nt(i,r,e.playback??{},this.source,this.path(e.id),{...this.o,media:this.native.has(e.id)?this.o.media:void 0},e.source.kind==="playlist"?e.source.item:void 0);this.loops.push(a),s.push(a.ready);for(let d of a.unplayed)n.add(d)}else if(e.type==="app"){let r=e.app?this.source.app?.(e.app):null;r?s.push(this.mountApp(e,i,r)):n.add(`app ${e.app?.id??e.id}`)}return i}place(e,n){cn(e.el,e.e,ln(e.motion,e.e.frame,n,this.reduced),e.text);let s=e.e.type==="text"?e.el.firstElementChild:null;s&&qe(s)}loadFonts(){return en(this.doc.fonts??[],e=>this.source.font(e))}async mountApp(e,n,s){let i=this.path(e.id),o=e.app,r=(e.appInfo??s.info)?.id,a={now:()=>this.heldAt??Date.now(),startedAt:()=>this.startedAt||Date.now()},d=(p,m)=>this.o.onApp?.({type:"app.failed",element:i,item:o,...r?{app:r}:{},reason:p,...m?{error:m}:{}}),l=this.o.isolate?{...this.o.isolate,fonts:this.doc.fonts,font:p=>this.source.font(p)}:void 0,c=await Qt(e,n,s,this.source,a,this.o.appDeadlineMs??5e3,p=>{this.disposed?p.dispose():this.apps.push({app:p,box:n,path:i,id:e.id,item:o})},p=>d("error",p),l);if(!("held"in c)){if("failed"in c){d(c.failed,c.error);return}c.late&&(this.late.push(i),this.o.onAppLate?.(i,o.id),d("late"))}}async start(e=Date.now()){this.startedAt=e,this.heldAt=void 0,this.running=!0,this.hooks(e);let n=()=>{if(!this.running)return;let s=Date.now(),i=s-this.startedAt;for(let o of this.animated)this.place(o,i);for(let o of this.apps)o.app.update(s);this.frame=requestAnimationFrame(n)};(this.animated.length||this.apps.length)&&(this.frame=requestAnimationFrame(n)),await Promise.all(this.loops.map(s=>s.start()))}hooks(e){let n=this.doc.animation?.clips??[],s=ae(n).clips,i=typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches;for(let o of this.apps){let a=n.map((f,k)=>({c:f,t:s[k]})).filter(f=>f.c.element===o.id&&(f.c.kind==="enter"||f.c.kind==="exit")).filter(f=>o.app.has(f.c.kind));if(!a.length)continue;let d=o.box,l=this.animated.findIndex(f=>f.el===d);l>=0&&(this.animated.splice(l,1),Object.assign(d.style,{opacity:"",transform:"",clipPath:""}));let c=(f,k,b)=>this.hook(o,f.kind,{startAt:k,preset:f.preset??"fade",durationMs:f.durationMs,easing:f.easing,delayMs:0,...f.staggerMs?{staggerMs:f.staggerMs}:{},...b!==void 0?{page:b}:{},reducedMotion:i},d);for(let{c:f,t:k}of a)c(f,e+k.startMs);let p=o.app.paging();if(!p)continue;let m=a.find(f=>f.c.kind==="enter")?.c,y=a.find(f=>f.c.kind==="exit")?.c,A=pn(p.count,p.ms,Ct(this.doc,this.source).durationMs);for(let f=1;f<A.length;f++){let k=e+A[f].startMs;y&&c(y,k-y.durationMs,A[f-1].page),m&&c(m,k,A[f].page)}}}hook(e,n,s,i){let o=!1;e.app.hook(n,s).finally(()=>{o=!0});let r=s.startAt+s.durationMs;this.timers.push(setTimeout(()=>{o||!this.running||(this.overran.includes(e.path)||(this.overran.push(e.path),this.o.onAppOverran?.(e.path,e.item.id),this.o.onApp?.({type:"app.failed",element:e.path,item:e.item,reason:"overran"})),n==="exit"&&(i.style.visibility="hidden"))},Math.max(0,r+mi-Date.now())))}pause(){this.heldAt=Date.now();for(let e of this.loops)e.pause();for(let e of this.apps)e.app.update(this.heldAt)}seek(e){this.running=!1,cancelAnimationFrame(this.frame);for(let n of this.loops)n.pause();for(let n of this.animated)this.place(n,e);this.heldAt=this.startedAt+e;for(let n of this.apps)n.app.update(this.heldAt)}resume(){this.heldAt=void 0;for(let e of this.loops)e.resume()}paging(e){return this.apps.find(n=>n.id===e)?.app.paging()??null}dispose(){this.running=!1,this.disposed=!0,cancelAnimationFrame(this.frame);for(let e of this.timers)clearTimeout(e);for(let e of this.loops)e.stop();for(let e of this.apps)e.app.dispose();this.el.remove()}},hn=t=>Math.min(Math.max(0,t),2**31-1),nt=class{constructor(e,n,s,i,o,r,a){h(this,"region",e);h(this,"playback",s);h(this,"source",i);h(this,"path",o);h(this,"o",r);h(this,"list",a);h(this,"ready");h(this,"unplayed",[]);h(this,"renderer");h(this,"slides");h(this,"order",[]);h(this,"pass",0);h(this,"at",0);h(this,"timer");h(this,"current");h(this,"shownAt",0);h(this,"remaining");h(this,"stopped",!1);h(this,"ended",!1);h(this,"empty",!1);h(this,"open");h(this,"pages");this.renderer=new xe(e,{media:r.media,fit:s.fit,path:o,onItem:r.onItem,onPassEnd:r.onPassEnd,onApp:r.onApp}),s.transition==="slide"&&this.unplayed.push("transition slide");let d=s.transition==="fade"?{type:"crossfade"}:void 0,l=n.map(c=>i.slide(c,{imageDurationMs:s.imageDurationMs,transition:d,region:!0})).filter(c=>!!c);this.slides=l.length===1&&l[0].untilEnd&&(s.onEnd??"loop")==="loop"?[{...l[0],untilEnd:!1,loop:!0}]:l,this.renderer.onMediaEnd(c=>{c===this.current&&!this.stopped&&(clearTimeout(this.timer),this.next("completed"))}),this.ready=this.newPass().then(()=>this.slides.length?this.renderer.prepare(this.slides[this.order[0]]):void 0).catch(()=>{})}async newPass(){let e=this.slides.length;this.order=this.playback.order==="shuffle"?await un(this.source.deviceId,this.path,this.pass,e):[...Array(e).keys()],this.at=0}async start(){this.slides.length&&await this.first()}bounds(e){return[this.source.bounds?.(e.item),this.list&&this.source.bounds?.(this.list)]}seek(e){let n=Date.now(),s=e;for(;s<this.order.length&&!this.bounds(this.slides[this.order[s]]).every(i=>Lt(i,n));)s++;return s}async first(){if(this.at=this.seek(0),this.at<this.order.length){this.empty=!1,await this.show();return}this.empty=!0,this.current=void 0,this.renderer.standby();let e=Date.now(),n=this.slides.flatMap(s=>this.bounds(s).map(i=>i?.from??0)).filter(s=>s>e);clearTimeout(this.timer),n.length&&(this.timer=setTimeout(()=>{!this.stopped&&!this.ended&&this.first()},hn(Math.min(...n)-e)))}async show(){let e=this.slides[this.order[this.at]];this.current=e;try{await this.renderer.show(e)}catch(s){if(this.stopped)return;this.o.onItem?.({type:"item.error",element:this.path,item:e.item,blob:e.blob.hash,...Ye(s)}),this.o.onItem?.({type:"item.end",element:this.path,item:e.item,blob:e.blob.hash,endedAt:Date.now(),reason:"error"}),this.schedule(fi);return}if(this.stopped)return;if(this.shownAt=Date.now(),this.open=e,this.o.onItem?.({type:"item.start",element:this.path,item:e.item,blob:e.blob.hash,contentType:e.blob.contentType,startedAt:this.shownAt,durationMs:e.durationMs}),e.pages&&(this.pages=new ie(this.renderer,e,this.shownAt,!1)),!(this.slides.length===1&&e.loop))this.schedule(e.durationMs+(e.untilEnd?5e3:0));else{let s=this.bounds(e).flatMap(i=>i?.until??[]);s.length&&this.schedule(Math.min(...s)-this.shownAt)}let n=this.peek();n&&n!==e&&this.renderer.prepare(n).catch(()=>{})}peek(){let e=this.seek(this.at+1);if(e<this.order.length)return this.slides[this.order[e]];if(this.o.endsWith!==this.path)return this.playback.onEnd==="hold"||this.playback.onEnd==="hide"||this.playback.order==="shuffle"?void 0:this.slides[this.order[0]]}schedule(e){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.next("completed")},hn(e))}async next(e){if(this.stopped||this.ended||!this.current)return;let n=()=>this.endOpen(e),s=this.seek(this.at+1);if(s<this.order.length){this.at=s,n(),await this.show();return}let o=this.o.onPassEnd?.(this.path)==="hold"?"hold":this.playback.onEnd??"loop";if(o==="hold"){this.ended=!0;return}if(o==="hide"){this.ended=!0,n(),this.renderer.standby(),this.region.style.visibility="hidden";return}this.pass++,await this.newPass(),n(),await this.first()}pause(){clearTimeout(this.timer),this.current&&this.remaining===void 0&&(this.remaining=Math.max(0,this.current.durationMs-(Date.now()-this.shownAt))),this.pages?.pause(),this.renderer.pause()}resume(){if(this.empty&&!this.ended&&!this.stopped){this.first();return}this.remaining===void 0||this.ended||(this.renderer.resume(),this.pages?.resume(),this.shownAt=Date.now()-(this.current.durationMs-this.remaining),this.schedule(this.remaining),this.remaining=void 0)}endOpen(e){this.pages?.stop(),this.pages=void 0;let n=this.open;n&&(this.open=void 0,this.o.onItem?.({type:"item.end",element:this.path,item:n.item,blob:n.blob.hash,endedAt:Date.now(),reason:e}))}stop(){this.endOpen("stopped"),this.stopped=!0,clearTimeout(this.timer),this.renderer.standby();for(let e of this.slides)this.renderer.release(e)}};var st=t=>t!=null&&t!==""&&!(Array.isArray(t)&&t.length===0);function gn(t,e,n,s=[]){let i=t[n];if(!i)return!1;if(!i.when)return!0;if(s.includes(n))return!1;let o=r=>String(r);for(let[r,a]of Object.entries(i.when)){if(!t[r]||r===n)return!1;let l=gn(t,e,r,[...s,n])?e[r]!==void 0?e[r]:t[r].default:void 0,c=Array.isArray(l)?l.map(o):st(l)?[o(l)]:[];if(a.set!==void 0&&a.set!==st(l)||a.anyOf&&!(st(l)&&!Array.isArray(l)?a.anyOf.map(o).includes(o(l)):c.some(p=>a.anyOf.map(o).includes(p)))||a.allOf&&!a.allOf.map(o).every(p=>c.includes(p))||a.noneOf&&c.some(p=>a.noneOf.map(o).includes(p)))return!1}return!0}function yn(t,e){return Object.keys(t).filter(n=>gn(t,e,n))}var da=re.apiVersion,bn=new Map(re.commands.map(t=>[t.name,t]));var W=class extends Error{constructor(n,s){super(s);h(this,"code",n)}};function wn(t,e){let n=e.capability??"read",s={kind:"app",app:e.id,instance:e.instance},i=new Set,o=!1,r=async(a,d={})=>{if(o)throw new W("NOT_ALLOWED","This app has been removed from the screen.");let l=bn.get(a);if(!l)throw new W("UNKNOWN",`There is no command ${a}.`);if(l.apps==="none")throw new W("NOT_ALLOWED",`Apps cannot call ${a}.`);if(l.apps==="control"&&n!=="control")throw new W("NOT_ALLOWED",`${a} needs capabilities.player: control in ${e.id}'s definition.`);let c=await t.call(a,d,s);if(c.ok)return c.result;if(c.code==="PREVIEW")return{applied:!1,reason:"preview"};throw new W(c.code,c.message)};return{app:e.id,instance:e.instance,capability:n,status:()=>t.status(),get:a=>t.status()[a],on(a,d){if(o)return()=>{};let l=t.on(a,d),c=()=>{l(),i.delete(c)};return i.add(c),c},time:()=>r("time.now"),call:r,play:()=>r("player.play"),pause:()=>r("player.pause"),stop:()=>r("player.stop"),next:()=>r("player.next"),previous:()=>r("player.previous"),finish:()=>r("app.finish"),dispose(){o=!0;for(let a of[...i])a()}}}var vn=["none","fade","scale","slide-up","slide-left"],Se=[["1920 \xD7 1080",1920,1080],["1080 \xD7 1920",1080,1920],["3840 \xD7 2160",3840,2160]],gi=[.25,.5,1,2,4],pe=3e4,En=5e3,yi={id:"med_preview",version:1},u={width:1920,height:1080,props:{},touched:new Set,playing:!0,speed:1,t:0,enter:"none",exit:"none",clipMs:800,timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone,locale:navigator.language||"en-US",screen:{name:"Preview screen",lat:49.9,lon:-97.14,fields:"{}"},samples:{},tables:new Map,people:[]},I={frames:"",tables:{},controller:{status:{},events:{}}},B={people:new Set,player:new Map},Ce=[],rt=new Map,S=null,ot=null,at=null,Sn=-1,V=null,le=0,X={wall:0,t:0},Te=0,Pe=[],$=t=>document.getElementById(t),kn=$("stage"),bi=$("controls"),xn=$("report");function g(t,e={},...n){let s=document.createElement(t);for(let[i,o]of Object.entries(e))o==null||o===!1||(i.startsWith("on")&&typeof o=="function"?s.addEventListener(i.slice(2),o):i==="value"||i in s&&typeof o!="string"?s[i]=o:s.setAttribute(i,o===!0?"":String(o)));for(let i of n)i!=null&&s.append(i);return s}var wi=0;function E(t,e,n=!1){return e.id||(e.id=`c${++wi}`),n?g("div",{class:"field inline"},e,g("label",{for:e.id},t)):g("div",{class:"field"},g("label",{for:e.id},t),e)}function z(t,e,n){let s=g("select",{onchange:()=>n(s.value)},...t.map(i=>{let[o,r]=Array.isArray(i)?i:[i,i];return g("option",{value:o,selected:o===e},r)}));return s}async function An(){let[t,e,n,s]=await Promise.all([fetch("/app.json").then(r=>r.json()),fetch("/app.js"),fetch("/samples.json").then(r=>r.json()),fetch("/config.json").then(r=>r.json())]);I=s;for(let[r,a]of Object.entries(I.tables))u.tables.has(dt(r))||Tn(r,a);S=t,Sn=Number(e.headers.get("x-preview-version")??-1),at=e.ok?null:await e.text(),ot=e.ok?new Uint8Array(await e.arrayBuffer()):null;let i=S.params??{},o={};for(let[r,a]of Object.entries(i))o[r]=u.touched.has(r)?u.props[r]:a.default;u.props=o;for(let[r,a]of Object.entries(n))r in u.samples||(u.samples[r]=a);Pn(),document.title=`${S.name}: Wyndlass app preview`,$("title").textContent=S.name,$("subtitle").textContent=`${S.id} ${S.version}`}var dt=t=>`tbl_${(t.replace(/\.(json|csv)$/i,"").toLowerCase().replace(/[^a-z0-9]+/g,"")||"table").slice(0,26).padEnd(26,"0")}`;function vi(t){let e=[],n=[],s="",i=!1;for(let o=0;o<t.length;o++){let r=t[o];if(i){r==='"'&&t[o+1]==='"'?(s+='"',o++):r==='"'?i=!1:s+=r;continue}r==='"'?i=!0:r===","?(n.push(s),s=""):r===`
|
|
5
|
+
`||r==="\r"?(r==="\r"&&t[o+1]===`
|
|
6
|
+
`&&o++,n.push(s),e.push(n),n=[],s=""):s+=r}return(s||n.length)&&(n.push(s),e.push(n)),e.filter(o=>o.some(r=>r!==""))}function ki(t,e){let n=dt(t),s=(o,r)=>({table:n,version:1,rows:r,columns:o.map(a=>({key:a,label:a,type:r.length&&r.every(d=>d[a]===""||d[a]===void 0||Number.isFinite(Number(d[a])))?"number":"text"}))});if(/\.csv$/i.test(t)){let[o=[],...r]=vi(e),a=o.map(c=>c.trim()),d=r.map(c=>Object.fromEntries(a.map((p,m)=>[p,c[m]??""]))),l=s(a,d);for(let c of l.columns)if(c.type==="number")for(let p of d)p[c.key]!==""&&(p[c.key]=Number(p[c.key]));return l}let i=JSON.parse(e);if(i&&typeof i=="object"&&"columns"in i&&"rows"in i)return{...i,table:n};if(Array.isArray(i))return s([...new Set(i.flatMap(o=>Object.keys(o)))],i);throw new Error(`${t} is not a table: a snapshot, an array of rows, or CSV`)}function Tn(t,e){try{u.tables.set(dt(t),ki(t,e)),rt.delete(t)}catch(n){rt.set(t,String(n.message))}}function Pn(){let t=[...u.tables.keys()][0];if(!(!t||!S))for(let[e,n]of Object.entries(S.params??{}))n.type==="datatable"&&!u.props[e]&&(u.props[e]=t,u.touched.add(e))}function xi(){return{measuring:!0,now:()=>[{source:"preview",present:u.people.length,longestDwellS:u.people.length?Math.round((Date.now()-Math.min(...u.people))/1e3):0}],on(t){return B.people.add(t),()=>{B.people.delete(t)}}}}function it(t){if(t==="enter"&&u.people.push(Date.now()),t==="leave"&&!u.people.length)return;let e=t==="leave"?u.people.shift():0,n=t==="leave"?{type:"leave",source:"preview",dwellS:Math.round((Date.now()-e)/1e3)}:{type:t,source:"preview"};for(let s of B.people)s([n]);$("people-count").textContent=`${u.people.length} in view`}var Ai={status:()=>I.controller.status,on(t,e){let n=B.player.get(t)??new Set;return n.add(e),B.player.set(t,n),()=>{n.delete(e)}},async call(t,e,n){return Ce.push(`controller: ${n.app??"the app"} called ${t}, not applied (a preview)`),Y({}),{ok:!1,code:"PREVIEW",message:"a preview"}}},Mi={time:"time.now",play:"player.play",pause:"player.pause",stop:"player.stop",next:"player.next",previous:"player.previous",finish:"app.finish"};function Ei(t){let e=s=>i=>{throw Ce.push(`controller: ${s} refused, ${i.code??"error"}: ${i.message}`),Y({}),i},n={...t,call:(s,i)=>t.call(s,i).catch(e(s))};for(let[s,i]of Object.entries(Mi))n[s]=()=>t[s]().catch(e(i));return n}function Si(t){for(let e of[t.type,"*"])for(let n of B.player.get(e)??[])n(t)}function Ti(){return new Me({base:()=>location.origin,token:()=>{},fetch:(async t=>{let e=new URL(String(t),location.origin);if(e.pathname.endsWith("/v0/image"))return fetch("/picture.svg");let n=decodeURIComponent(e.pathname.split("/").pop()??"");if(!(n in u.samples))return new Response(`the preview has no answer for ${n}: declare it in app.json's data`,{status:404});let s=Date.now();return new Response(JSON.stringify({source:n,params:Object.fromEntries(e.searchParams),fetchedAt:new Date(s).toISOString(),expiresAt:new Date(s+36e5).toISOString(),stale:!1,data:u.samples[n]}))})})}function Pi(){let t=[...u.enter!=="none"?[{element:"app",kind:"enter",preset:u.enter,start:{kind:"at",ms:0},durationMs:u.clipMs,easing:"ease-out"}]:[],...u.exit!=="none"?[{element:"app",kind:"exit",preset:u.exit,start:{kind:"at",ms:pe-u.clipMs},durationMs:u.clipMs,easing:"ease-in"}]:[]];return{profiles:[{id:"preview",width:u.width,height:u.height}],elements:[{type:"app",id:"app",frame:{x:0,y:0,w:u.width,h:u.height},app:yi,props:u.props,...S.slots&&u.tables.size?{binding:{table:[...u.tables.keys()][0]}}:{},...S.capabilities?.sensors?{sensors:S.capabilities.sensors}:{},appInfo:{id:S.id,element:S.element,player:S.capabilities?.player==="control"?"control":"read"}}],animation:{duration:{kind:"fixed",ms:pe},clips:t}}}async function Le(){let t=++le;if(cancelAnimationFrame(Te),V?.dispose(),V=null,kn.replaceChildren(),Pe.length=0,Ce.length=0,!S||!ot){Y({building:!0});return}let e={};try{e=JSON.parse(u.screen.fields||"{}")}catch{}let n=ot,s={deviceId:"preview",slide:()=>null,playlist:()=>null,font:()=>{},app:()=>({hash:`preview-${le}`,bytes:n}),data:Ti(),place:{lat:u.screen.lat,lon:u.screen.lon},screen:{name:u.screen.name,type:"preview",tags:[],path:[{name:"Preview",kind:"organization"}],fields:e},timeZone:u.timeZone,locale:u.locale,preview:!1,snapshot:a=>u.tables.get(a)??null,...I.frames?{}:{...S.capabilities?.sensors?.includes("impressions")?{impressions:xi()}:{},controllerFor:a=>Ei(wn(Ai,a))}};B.people.clear(),B.player.clear();let i=performance.now(),o=!1,r=new J(Pi(),s,kn,{appDeadlineMs:En,...I.frames?{isolate:{frames:I.frames,apps:()=>!0}}:{},onAppLate:()=>{o=!0},onApp:a=>{t===le&&(Pe.push(`app.failed: ${a.reason}${a.error?` (${a.error})`:""}`),Y({}))}});V=r,Y({waiting:!0}),await r.ready,t===le&&(Y({readyMs:o?void 0:Math.round(performance.now()-i),late:o}),await r.start(Date.now()-u.t),t===le&&(r.seek(u.t),X={wall:Date.now(),t:u.t},Li(),Ii()))}function Li(){cancelAnimationFrame(Te);let t=()=>{if(V){if(u.playing){if(u.t=X.t+(Date.now()-X.wall)*u.speed,u.t>=pe){u.t=0,Le();return}V.seek(u.t)}$("clock-seek").value=String(Math.round(u.t)),$("clock-time").textContent=`${(u.t/1e3).toFixed(1)} s`,Te=requestAnimationFrame(t)}};Te=requestAnimationFrame(t)}function Ii(){let t=$("pages"),e=V?.paging("app")??null;t.replaceChildren(e&&e.count>1?g("div",{},g("p",{"data-testid":"pages"},`${e.count} pages of ${(e.ms/1e3).toFixed(1)} s`),g("div",{class:"row"},...Array.from({length:e.count},(n,s)=>g("button",{onclick:()=>Ln(s*e.ms)},`Page ${s+1}`)))):g("p",{"data-testid":"pages"},"One page"))}function Ln(t){u.t=Math.max(0,Math.min(pe-1,t)),X={wall:Date.now(),t:u.t},V?.seek(u.t)}function Y(t){let e=[];if(at?e.push(g("pre",{"data-testid":"build-error"},`The module does not build:
|
|
7
|
+
${at}`)):t.building&&e.push(g("span",{},"Building\u2026")),t.waiting&&e.push(g("span",{"data-testid":"ready"},"Waiting for ready()\u2026")),t.late)e.push(g("span",{class:"late","data-testid":"ready"},`Late: not ready within ${En/1e3} s. A screen reports app.failed (late) and shows the app as it is.`));else if(t.readyMs!==void 0)e.push(g("span",{class:"ok","data-testid":"ready"},`Ready in ${t.readyMs} ms`));else if(!t.waiting&&!t.building){let n=xn.querySelector('[data-testid="ready"]');n&&e.push(n)}for(let n of Pe)e.push(g("span",{class:"failed","data-testid":"failure"},n));I.frames&&Pe.some(n=>/SecurityError/.test(n))&&e.push(g("span",{class:"warn","data-testid":"frame-why"},"In its frame, as in the CMS, an app has no storage, cookies or parent page: localStorage, sessionStorage, document.cookie and parent's document throw SecurityError. Keep nothing there that matters; a screen may lose it too."));for(let n of Ce.slice(-5))e.push(g("span",{"data-testid":"command"},n));xn.replaceChildren(...e)}function Ci(t,e){let n=u.props[t],s=o=>{u.props[t]=o,u.touched.add(t),In(),L()},i=e.label??t;if(e.type==="boolean")return E(i,g("input",{type:"checkbox",checked:n===!0,onchange:o=>s(o.target.checked)}),!0);if(e.type==="enum"){let o=(e.values??[]).map(r=>r&&typeof r=="object"?[String(r.value),String(r.label??r.value)]:String(r));return E(i,z(o,String(n??""),s))}if(e.type==="number")return E(i,g("input",{type:"number",value:n===void 0?"":String(n),min:e.min,max:e.max,step:e.step??"any",onchange:o=>{let r=o.target.value;s(r===""?void 0:Number(r))}}));if(["string","text","color","url","datetime","duration","secret"].includes(e.type)){let o=e.type==="text"?g("textarea",{value:String(n??""),onchange:r=>s(r.target.value)}):g("input",{type:"text",value:String(n??""),placeholder:e.type==="color"?"#rrggbb or #rrggbbaa":void 0,onchange:r=>s(r.target.value)});return E(i,o)}return e.type==="datatable"?E(i,z(["",...u.tables.keys()],String(n??""),s)):E(`${i} (JSON)`,g("textarea",{value:n===void 0?"":JSON.stringify(n,null,2),onchange:o=>{let r=o.target.value.trim();try{s(r?JSON.parse(r):void 0)}catch{s(r)}}}))}function In(){let t=$("params"),e=S?.params??{},n=new Set(yn(e,u.props));t.replaceChildren(...Object.entries(e).filter(([s])=>n.has(s)).map(([s,i])=>Ci(s,i))),t.children.length||t.append(g("p",{},"No params."))}var Mn;function L(){clearTimeout(Mn),Mn=setTimeout(()=>{Le()},150)}function Ie(){let t=!Se.some(([,r,a])=>r===u.width&&a===u.height),e=g("input",{type:"number",value:String(u.width),min:64,max:7680,onchange:r=>{u.width=Number(r.target.value)||1920,L()}}),n=g("input",{type:"number",value:String(u.height),min:64,max:7680,onchange:r=>{u.height=Number(r.target.value)||1080,L()}}),s=g("button",{type:"button","aria-pressed":String(!u.playing),onclick:()=>{u.playing=!u.playing,s.textContent=u.playing?"Pause":"Play",s.setAttribute("aria-pressed",String(!u.playing)),X={wall:Date.now(),t:u.t}}},u.playing?"Pause":"Play"),i=g("input",{id:"clock-seek",type:"range",min:0,max:pe,step:100,value:String(u.t),oninput:r=>Ln(Number(r.target.value))}),o=[g("h2",{},"Screen"),E("Size",z([...Se.map(([r])=>r),"Custom"],t?"Custom":Se.find(([,r,a])=>r===u.width&&a===u.height)[0],r=>{let a=Se.find(([d])=>d===r);a&&(u.width=a[1],u.height=a[2]),Ie(),L()})),t?g("div",{class:"row"},E("Width",e),E("Height",n)):null,g("div",{class:"row"},E("Time zone",g("input",{type:"text",value:u.timeZone,onchange:r=>{u.timeZone=r.target.value||"UTC",L()}})),E("Locale",g("input",{type:"text",value:u.locale,onchange:r=>{u.locale=r.target.value||"en-US",L()}}))),E("Screen's name",g("input",{type:"text",value:u.screen.name,onchange:r=>{u.screen.name=r.target.value,L()}})),g("div",{class:"row"},E("Latitude",g("input",{type:"number",step:"any",value:String(u.screen.lat),onchange:r=>{u.screen.lat=Number(r.target.value),L()}})),E("Longitude",g("input",{type:"number",step:"any",value:String(u.screen.lon),onchange:r=>{u.screen.lon=Number(r.target.value),L()}}))),E("Screen's fields (JSON)",g("textarea",{value:u.screen.fields,onchange:r=>{u.screen.fields=r.target.value,L()}})),g("h2",{},"Clock"),g("div",{class:"row"},s,E("Speed",z(gi.map(r=>[String(r),`${r}\xD7`]),String(u.speed),r=>{X={wall:Date.now(),t:u.t},u.speed=Number(r)})),g("span",{id:"clock-time"},"0.0 s")),E("Time in the layout",i),g("h2",{},"Enter and exit"),g("div",{class:"row"},E("Enter",z(vn,u.enter,r=>{u.enter=r,L()})),E("Exit",z(vn,u.exit,r=>{u.exit=r,L()}))),E("Each lasts (ms)",g("input",{type:"number",min:0,max:5e3,step:100,value:String(u.clipMs),onchange:r=>{u.clipMs=Number(r.target.value)||0,L()}})),g("div",{id:"pages"}),g("h2",{},"Params"),g("div",{id:"params"}),I.frames?g("p",{"data-testid":"framed"},`In a frame on ${I.frames}, as in the CMS: no storage, cookies, parent page, controller or impressions.`):null,g("h2",{},"Tables"),...[...u.tables.values()].map(r=>g("p",{"data-testid":"table"},`${r.table}: ${r.rows.length} rows, ${r.columns.map(a=>a.key).join(", ")}`)),...[...rt].map(([r,a])=>g("p",{class:"failed","data-testid":"table-error"},`${r}: ${a}`)),E("Add a table (JSON or CSV)",g("input",{type:"file",accept:".json,.csv",onchange:async r=>{let a=r.target.files?.[0];a&&(Tn(a.name,await a.text()),Pn(),Ie(),L())}})),...S?.capabilities?.sensors?.includes("impressions")&&!I.frames?[g("h2",{},"People at the screen"),g("div",{class:"row"},g("button",{type:"button",onclick:()=>it("enter")},"Someone arrives"),g("button",{type:"button",onclick:()=>it("look")},"Someone looks"),g("button",{type:"button",onclick:()=>it("leave")},"Someone leaves")),g("p",{id:"people-count"},`${u.people.length} in view`)]:[],...I.frames?[]:[g("h2",{},"The player's controller"),g("p",{},`The app may ${S?.capabilities?.player==="control"?"read it and call its commands":"read it (capabilities.player: read)"}. Commands are not applied here.`),...Object.keys(I.controller.events).length?[E("Event",z(Object.keys(I.controller.events),Object.keys(I.controller.events)[0],r=>{$("event-json").value=JSON.stringify(I.controller.events[r],null,2)})),E("Its JSON",g("textarea",{id:"event-json",rows:6,value:JSON.stringify(Object.values(I.controller.events)[0],null,2)})),g("button",{type:"button",onclick:()=>{try{Si(JSON.parse($("event-json").value))}catch{}}},"Send to the app")]:[]],g("h2",{},"Data"),...Object.keys(u.samples).map(r=>E(`${r} answer's data (JSON)`,g("textarea",{"data-source":r,value:JSON.stringify(u.samples[r],null,2),rows:8,onchange:a=>{try{u.samples[r]=JSON.parse(a.target.value),L()}catch{}}}))),Object.keys(u.samples).length?null:g("p",{},"The app declares no data source with a sample (weather, news or calendar).")];bi.replaceChildren(...o.filter(r=>r!==null)),In()}async function Ri(){await An(),Ie(),await Le();let t=new EventSource("/events"),e=()=>{An().then(()=>(Ie(),Le()))};t.addEventListener("hello",n=>{JSON.parse(n.data).version!==Sn&&e()}),t.addEventListener("reload",e),window.addEventListener("resize",()=>L())}Ri();})();
|
package/dist/qr.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The QR code for `text`: its modules row by row, true where dark. Error correction M (15%: a screen seen at an angle,
|
|
3
|
+
* a smudge), the smallest version that holds it; byte mode, so any URL fits.
|
|
4
|
+
*/
|
|
5
|
+
export declare function qrModules(text: string): boolean[][];
|