@wyndlass/app-sdk 1.3.0 → 1.4.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/README.md CHANGED
@@ -63,7 +63,7 @@ Its controls:
63
63
  - **Data:** each data source's answer, from samples you can edit in the page. `--data <dir>` reads `<source>.json` there instead (the answer's `data`, or a whole answer). The preview reaches no data service. A source the definition doesn't declare gets no answer, as on a screen.
64
64
  - **Tables:** each file in `<dir>/tables/`, or one you add in the page. A file may be CSV, a JSON array of rows, or a snapshot. A CSV's header gives its columns' labels, and their keys are made as Wyndlass makes a table's (`Now serving` is `now_serving`), so a `column` param holds the key it will on screens. A `datatable` param chooses among the tables, a `column` param among the chosen table's columns, and an app with `slots` is bound to the first table. Edit a table's rows in the page, or change its file, and the app gets them as it plays, as a screen gives a table's new version: frozen, and without making the app again. The report says how many times the app was made.
65
65
  - **The people at the screen,** for an app that declares the `impressions` sensor: someone arrives, looks, or leaves.
66
- - **The player's controller,** for what `capabilities.player` allows. The page sends the app the player's events, each one editable. A command the app may give is answered "not applied", and one it may not is refused, as on a screen.
66
+ - **The player's controller,** for what `capabilities.player` allows. The page sends the app the player's events, each one editable, and the controller's status follows them, as a screen's does. It sends a command by name, with an argument, as `apps.sendCommand` does. A command the app may give is answered "not applied", and one it may not is refused, as on a screen.
67
67
 
68
68
  **What a screen would report** is shown beside the app: how long it took to say it was ready, or that it was late, and each failure with its reason.
69
69
 
@@ -148,7 +148,9 @@ export default class QueueBoard extends WyndlassApp {
148
148
  - `data(this, source, params)`, the data service's answer;
149
149
  - `kept(this, source, params)`, the answer the screen last kept, at once;
150
150
  - `image(this, path)`, `snapshot(this, table)`, `controllerOf(this)` and `impressionsOf(this)`;
151
+ - `onCommand(this, name, handler)`, each command of that name sent to the screen's apps (`apps.sendCommand`), with its argument. A command is an event: the same one sent twice runs twice;
151
152
  - `contextOf(this)`, for the rest.
153
+ - **Follow the player with the signals or hooks** (`playbackSignal`, `usePlayback` and the rest, below), never by reading `controllerOf(this).status()` at each frame.
152
154
  - **A table's rows change while the app plays.** A screen gives the app a table's new rows within about a second of the save, without making it again. Read them where you draw: `snapshot()` in `draw()`, `useSnapshot`, or `tableSignal`. Never read them once in `mount()` and keep them. On a screen they are the same object until they change, and frozen: copy rows before you sort or change them.
153
155
  - **Helpers:** `fit`, `typographyCss`, `color` and `escape`, and the presets' curves `easingOf`, `cubicBezier` and `staggerTimes`.
154
156
  - **Types:**
@@ -184,12 +186,14 @@ export default reactApp(Board, { css });
184
186
  - **`usePaging(count, ms)`:** the page showing now, rendering when it turns. It also tells the host the pages.
185
187
  - **`useData(source, params)`:** at once the answer the screen kept, then the service's, asked again every 10 minutes. `error` is set once an ask fails with nothing to show.
186
188
  - **`useSnapshot(table)`:** a data table's snapshot, as `snapshot` gives it. The component renders when the rows change: when the screen gets a table's new version, or when a binding's rows, chosen on the layout's clock, change. On players before Wyndlass's live rows (WP-0149), which make the app again when a table changes, it renders at every frame, as before.
189
+ - **`usePlayback()`, `useConnection()`, `useAudio()`, `usePlaying()`:** the player's state (starting, playing, paused, stopped, idle or error), online or offline, the volume and whether it is muted, and the items playing. Each renders only when its value changes. Each is `null` where the app has no controller: in the designer and the CMS's previews.
190
+ - **`useCommand(name, handler)`:** each command of that name sent to the screen's apps, with its argument, while the component is mounted.
187
191
 
188
192
  The hooks reach the app's context through the SDK's own module, never through React's tree, so nothing another app could use hangs off the shadow root.
189
193
 
190
194
  ## Signals (`@wyndlass/app-sdk/signals`)
191
195
 
192
- For an app that updates its DOM when its rows change, rather than at every frame:
196
+ For an app that updates its DOM when its rows or the player's state change, rather than at every frame:
193
197
 
194
198
  ```ts
195
199
  import { WyndlassApp } from "@wyndlass/app-sdk";
@@ -208,6 +212,7 @@ export default class Queue extends WyndlassApp {
208
212
  ```
209
213
 
210
214
  - **`tableSignal(this, table)`:** a read-only [Preact signal](https://github.com/preactjs/signals) of the table's snapshot. Its value changes when the rows do, and is the same object until then. Make it in `mount()`.
215
+ - **`playbackSignal(this)`, `connectionSignal(this)`, `audioSignal(this)`, `nowSignal(this)`:** the player's state, online or offline, the volume and whether it is muted, and the items playing, as read-only signals. Each is set only when its value changes, and is `null` where the app has no controller. Make them in `mount()`.
211
216
  - **`effect`, `computed`, `batch`, `signal`, `untracked`:** Preact's, from the same copy. Import them from here, not from `@preact/signals-core`: an effect from another copy does not see these signals.
212
217
  - **Its own entry,** so an app that doesn't import it doesn't bundle it (about 2 KB gzipped when it does).
213
218
  - On players before WP-0149, the signal keeps the rows the app was made with: such a player makes the app again when a table changes.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Commands sent to the screen's apps (`apps.sendCommand`, the `app.command` event; WP-0150 decision 3). The main entry
3
+ * gives it; a module of its own, so the runtime that every app bundles is not changed by it.
4
+ */
5
+ import { type WyndlassApp } from "./index.js";
6
+ /**
7
+ * Calls `handler` with the argument of each command of this name sent to the screen's apps. A command is an event: the
8
+ * same one sent twice runs twice. Returns how to stop. Never called where the app has no controller (the designer).
9
+ */
10
+ export declare function onCommand(app: WyndlassApp, name: string, handler: (argument: Record<string, unknown> | undefined) => void): () => void;
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Commands sent to the screen's apps (`apps.sendCommand`, the `app.command` event; WP-0150 decision 3). The main entry
3
+ * gives it; a module of its own, so the runtime that every app bundles is not changed by it.
4
+ */
5
+ import { controllerOf } from "./index.js";
6
+ /**
7
+ * Calls `handler` with the argument of each command of this name sent to the screen's apps. A command is an event: the
8
+ * same one sent twice runs twice. Returns how to stop. Never called where the app has no controller (the designer).
9
+ */
10
+ export function onCommand(app, name, handler) {
11
+ const controller = controllerOf(app);
12
+ if (!controller)
13
+ return () => { };
14
+ return controller.on("app.command", e => { if (e.type === "app.command" && e.name === name)
15
+ handler(e.argument); });
16
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * What an app follows of the player (WP-0150 decisions 1 and 2): a field of its controller's status, read again on the
3
+ * events that change it, and the same object until its value changes (compared as JSON). Null without a controller: in
4
+ * the designer, or on a player that gives none. Not one of the package's exports: signals.ts and react.ts make it public.
5
+ */
6
+ import type { PlayerAPIStatus } from "./contract/player-api-status.js";
7
+ import { type WyndlassApp } from "./index.js";
8
+ /** Each field an app can follow, and the events after which it is read again. */
9
+ declare const FIELDS: {
10
+ readonly playback: {
11
+ readonly events: readonly ["state.changed"];
12
+ readonly read: (s: PlayerAPIStatus) => import("./contract/player-api-status.js").State;
13
+ };
14
+ readonly connection: {
15
+ readonly events: readonly ["connection.changed"];
16
+ readonly read: (s: PlayerAPIStatus) => import("./contract/player-api-status.js").PlayerApiCommonConnection;
17
+ };
18
+ readonly audio: {
19
+ readonly events: readonly ["audio.changed"];
20
+ readonly read: (s: PlayerAPIStatus) => import("./contract/player-api-status.js").PlayerApiCommonAudio | null;
21
+ };
22
+ readonly now: {
23
+ readonly events: readonly ["item.start", "item.end"];
24
+ readonly read: (s: PlayerAPIStatus) => import("./contract/player-api-status.js").NowItem[];
25
+ };
26
+ };
27
+ export type Field = keyof typeof FIELDS;
28
+ export type Followed<K extends Field> = ReturnType<(typeof FIELDS)[K]["read"]> | null;
29
+ /** A field's value now, and who to tell when it changes, as useSyncExternalStore and a signal take them. */
30
+ export interface Following<T> {
31
+ get(): T;
32
+ subscribe(listener: () => void): () => void;
33
+ }
34
+ export declare function follow<K extends Field>(app: WyndlassApp, field: K): Following<Followed<K>>;
35
+ export {};
@@ -0,0 +1,59 @@
1
+ import { controllerOf } from "./index.js";
2
+ /** Each field an app can follow, and the events after which it is read again. */
3
+ const FIELDS = {
4
+ playback: { events: ["state.changed"], read: (s) => s.state },
5
+ connection: { events: ["connection.changed"], read: (s) => s.connection },
6
+ audio: { events: ["audio.changed"], read: (s) => s.audio ?? null },
7
+ now: { events: ["item.start", "item.end"], read: (s) => s.now },
8
+ };
9
+ export function follow(app, field) {
10
+ const controller = controllerOf(app);
11
+ const { events, read } = FIELDS[field];
12
+ const now = () => {
13
+ // A controller not yet started throws; the field is unknown until it has.
14
+ try {
15
+ return controller ? (read(controller.status()) ?? null) : null;
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ };
21
+ let value = now(), text = JSON.stringify(value);
22
+ const listeners = new Set();
23
+ let unheard = [];
24
+ /** Reads the field again, keeping the same object unless its value changed; says whether it did. */
25
+ const reread = () => {
26
+ const next = now(), json = JSON.stringify(next);
27
+ if (json === text)
28
+ return false;
29
+ value = next;
30
+ text = json;
31
+ return true;
32
+ };
33
+ const heard = () => { if (reread())
34
+ for (const l of [...listeners])
35
+ l(); };
36
+ return {
37
+ // With nobody listening, nothing says when it changes: it is read at each get.
38
+ get() { if (!listeners.size)
39
+ reread(); return value; },
40
+ subscribe(listener) {
41
+ if (!controller)
42
+ return () => { };
43
+ // The first listener: what changed while nobody listened is read now (useSyncExternalStore reads after subscribing).
44
+ if (!listeners.size) {
45
+ reread();
46
+ unheard = events.map(e => controller.on(e, heard));
47
+ }
48
+ listeners.add(listener);
49
+ return () => {
50
+ listeners.delete(listener);
51
+ if (listeners.size)
52
+ return;
53
+ for (const u of unheard)
54
+ u();
55
+ unheard = [];
56
+ };
57
+ },
58
+ };
59
+ }
package/dist/index.d.ts CHANGED
@@ -22,6 +22,7 @@ export type * as series from "./contract/data-series.js";
22
22
  export type * as weather from "./contract/data-weather.js";
23
23
  export type * as tables from "./contract/data-snapshot.js";
24
24
  export { cubicBezier, easingOf, staggerTimes } from "./easing.js";
25
+ export { onCommand } from "./commands.js";
25
26
  /** A default's type, widened: a prop that defaults to "digital" may be any string. */
26
27
  type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
27
28
  type Params = Record<string, string | number | boolean>;
package/dist/index.js CHANGED
@@ -27,6 +27,7 @@ import { cubicBezier, easingOf, staggerTimes } from "./easing.js";
27
27
  import { atFrame } from "./frames.js";
28
28
  export { ATTACH };
29
29
  export { cubicBezier, easingOf, staggerTimes } from "./easing.js";
30
+ export { onCommand } from "./commands.js";
30
31
  /** The contexts handed over through ATTACH, where no other script on the page can reach them. */
31
32
  const given = new WeakMap();
32
33
  // What acts or reads for an app is a function of this module, never a method or getter of the element: another app on
@@ -1,7 +1,7 @@
1
1
  /* @wyndlass/app-sdk's preview: player-core's host, compiled. MIT, (c) Northwynd Labs LLC. */
2
- "use strict";(()=>{var rs=Object.defineProperty;var is=(t,e,n)=>e in t?rs(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var h=(t,e,n)=>is(t,typeof e!="symbol"?e+"":e,n);var jt=Reflect.apply,qe=Object.getOwnPropertyDescriptor,Me=Object.getPrototypeOf,N=globalThis,x=(t,e,n)=>jt(t,e,n),$=(t,e)=>t?qe(t,e)?.get:void 0,We=(t,e)=>t?qe(t,e)?.set:void 0,C=t=>N[t]?.prototype,Ft=(t,e,n)=>jt(t,e,n);var ci=Date.now,ui=new TextEncoder,hi=new TextDecoder,mi=TextEncoder.prototype.encode,fi=TextDecoder.prototype.decode;var gi=$(Me(Uint8Array.prototype),"length");var os=Uint8Array,as=t=>new os(t),ls=[];for(let t=0;t<256;t++)ls[t]=(t<16?"0":"")+t.toString(16);var ds=Object.create(null);for(let t=0;t<64;t++)ds["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[t]]=t;var Tt=N.crypto?.subtle,ze=Tt?Me(Tt):void 0,_e=N.crypto,ps=_e?Me(_e).getRandomValues:void 0,yi=ze?.digest,bi=ze?.importKey,wi=ze?.sign;var Ht=t=>x(ps,_e,[as(t)]),vi={setTimeout:N.setTimeout,clearTimeout:N.clearTimeout,setInterval:N.setInterval,clearInterval:N.clearInterval};var ki=N.fetch;var oe=C("MessagePort"),xi=oe&&oe.postMessage,Mi=We(oe,"onmessage"),Ai=oe&&oe.start,Ve=C("MessageEvent"),Si=$(Ve,"data"),Ei=$(Ve,"ports"),Ti=$(Ve,"source");var Bt=C("EventTarget"),Pi=Bt?.addEventListener,Li=Bt?.removeEventListener;var Ii=N.Worker,ve=C("Worker"),Ci=ve&&ve.postMessage,Ri=We(ve,"onmessage"),Ni=We(ve,"onerror");var Pt=N.Blob,ke=N.URL,Lt=ke?.createObjectURL,cs=ke?.revokeObjectURL,Ke=Pt&&Lt?{make:(t,e)=>x(Lt,ke,[new Pt(t,{type:e})]),revoke:t=>x(cs,ke,[t])}:void 0,re=N.customElements,Ze=re?Me(re):void 0,us=Ze?.define,hs=Ze?.get,Je=re&&Ze?{define:(t,e)=>x(us,re,[t,e]),get:t=>x(hs,re,[t])}:void 0,Ut=N.document,xe=C("Document"),ms=xe?.createElement,fs=xe?.createElementNS,gs=WeakSet.prototype.add,Oi=WeakSet.prototype.has,Ye=new WeakSet,ys=new WeakSet,Ae=(t,e)=>(x(gs,t,[e]),e);function H(t){return Ae(Ye,x(ms,Ut,[t]))}var Se=t=>Ae(Ye,x(fs,Ut,["http://www.w3.org/2000/svg",t]));var Y=t=>Ae(ys,t);var ae=C("Node"),Xe=C("Element"),Ee=C("HTMLElement"),X=t=>$(ae,t),It=X("parentNode"),bs=X("firstChild"),ws=X("lastChild"),vs=X("nextSibling"),ks=X("nodeType"),xs=X("childNodes"),Ms=$(Xe,"localName"),As=ae?.appendChild,Ss=ae?.insertBefore,Es=ae?.removeChild,K=Xe,Ts=K?.getAttribute,Ps=K?.getAttributeNames,Ls=K?.setAttribute,Is=K?.removeAttribute,Cs=K?.matches,Rs=K?.attachShadow,Ns=K?.querySelectorAll,Os=C("HTMLDialogElement")?.close,$s=$(C("NodeList"),"length"),Ds=C("NodeList")?.item,Te=C("MutationRecord"),js=$(Te,"type"),Fs=$(Te,"target"),Hs=$(Te,"addedNodes"),Bs=$(Te,"removedNodes"),Ct=N.MutationObserver,Us=C("MutationObserver")?.observe,_t=xe?qe(xe,"adoptedStyleSheets"):void 0,Rt=_t?.get,Nt=_t?.set,_s=N.requestAnimationFrame,Ot=$(Ee,"style"),Pe=C("CSSStyleDeclaration"),qs=Pe?.getPropertyValue,Ws=Pe?.getPropertyPriority,zs=Pe?.setProperty,$t=Ee?.showPopover,Dt=Ee?.hidePopover,Vs=$(C("Event"),"target"),Ks=C("Event")?.stopImmediatePropagation,we=t=>{let e=[];for(let n=0,s=x($s,t,[]);n<s;n++)e[n]=x(Ds,t,[n]);return e},ie=Ct&&ae&&Xe&&Ee&&Pe?{parent:t=>x(It,t,[]),first:t=>x(bs,t,[]),last:t=>x(ws,t,[]),next:t=>x(vs,t,[]),isElement:t=>x(ks,t,[])===1,name:t=>x(Ms,t,[]),children:t=>we(x(xs,t,[])),append:(t,e)=>x(As,t,[e]),insert:(t,e,n)=>x(Ss,t,[e,n]),remove:t=>{let e=x(It,t,[]);e&&x(Es,e,[t])},attribute:(t,e)=>x(Ts,t,[e]),attributes:t=>x(Ps,t,[]),setAttribute:(t,e,n)=>x(Ls,t,[e,n]),removeAttribute:(t,e)=>x(Is,t,[e]),matches:(t,e)=>x(Cs,t,[e]),all:(t,e)=>we(x(Ns,t,[e])),closeDialog:t=>x(Os,t,[]),shadow:t=>x(Rs,t,[{mode:"closed"}]),style:(t,e)=>{let n=x(Ot,t,[]);return[x(qs,n,[e]),x(Ws,n,[e])]},setStyle:(t,e,n,s="")=>x(zs,x(Ot,t,[]),[e,n,s]),observer:t=>new Ct(t),observe:(t,e,n)=>x(Us,t,[e,n]),record:t=>({type:x(js,t,[]),target:x(Fs,t,[]),added:we(x(Hs,t,[])),removed:we(x(Bs,t,[]))}),adopted:Rt&&Nt?{count:t=>x(Rt,t,[]).length,clear:t=>x(Nt,t,[[]])}:void 0,frame:t=>x(_s,globalThis,[t]),popover:$t&&Dt?{show:t=>x($t,t,[]),hide:t=>x(Dt,t,[])}:void 0,target:t=>x(Vs,t,[]),stop:t=>x(Ks,t,[])}:void 0;function Ge(t){if(Ae(Ye,t),ie)for(let e of ie.children(t))ie.isElement(e)&&Ge(e);return t}var Qe=t=>new TextEncoder().encode(t);function Zs(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 et(t){return Zs(await crypto.subtle.digest("SHA-256",t))}var Js=["name","type","tags","location","site","district","region","organization","path","timeZone"],Ys=["text","date","currency","truncate"],qt={site:"SITE",district:"DISTRICT",region:"REGION",organization:"ORGANIZATION"};function Le(t,e,n,s){if(!t)return n==="timeZone"&&e?e:void 0;if(n==="fields")return s&&t.fields?.[s]||void 0;let r=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 r.at(-1)?.name;case"path":return r.length?r.map(o=>o.name).join(" \u203A "):void 0;case"timeZone":return e||void 0;default:return qt[n]?[...r].reverse().find(o=>o.kind===qt[n])?.name:void 0}}function Xs(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 tt(t,e){return(t.source==="screen"?Le(e.screen,e.timeZone,t.field,t.key):Xs(e.tables?.[t.table]?.rows?.[t.row??0]?.[t.column],t.format))??t.fallback??""}function Wt(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"),r=n.find(o=>!o[1].startsWith('"'))?.[1];return e[1]!==void 0?r?null:e[1].startsWith("fields.")?{source:"screen",field:"fields",key:e[1].slice(7),...s!==void 0?{fallback:s}:{}}:Js.includes(e[1])?{source:"screen",field:e[1],...s!==void 0?{fallback:s}:{}}:null:r&&!Ys.includes(r)?null:{source:"table",table:e[2],row:Number(e[3]),column:e[4]??e[5].replace(/\\(.)/g,"$1"),...r?{format:r}:{},...s!==void 0?{fallback:s}:{}}}function nt(t,e){return t.replace(/\{{2}([^{}]*)\}{2}/g,(n,s)=>{let r=Wt(s);return r?tt(r,e):n})}function Ie(t){return[...t.matchAll(/\{{2}([^{}]*)\}{2}/g)].some(e=>Wt(e[1])!==null)}function st(t,e){return Object.fromEntries(Object.entries(t).map(([n,s])=>[n,typeof s=="string"?nt(s,e):s]))}var rt=t=>t==null||t==="";function Vt(t,e,n,s,r,o){return Gs(t,e,n,s,{now:r,timeZone:o})}function Gs(t,e,n,s,r){if(n===null&&(e==="eq"||e==="ne"))return e==="eq"?rt(t):!rt(t);if(rt(t)||n===null||n===void 0)return!1;switch(s){case"number":case"currency":{let o=Number(t),i=Number(n);return typeof n!="boolean"&&n!==""&&Number.isFinite(o)&&Number.isFinite(i)&&it(e,o-i)}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"?Qs(r.now,r.timeZone).date:String(n),i=String(t).slice(0,10);return/^\d{4}-\d{2}-\d{2}$/.test(o)&&it(e,i<o?-1:i>o?1:0)}case"datetime":{let o=Date.parse(String(t)),i=n==="now"?r.now:Date.parse(String(n));return!Number.isNaN(o)&&!Number.isNaN(i)&&it(e,o-i)}default:{let o=String(t).toLowerCase(),i=String(n).toLowerCase();return e==="eq"?o===i:e==="ne"?o!==i:e==="contains"?o.includes(i):!1}}}function it(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 zt=new Map;function Qs(t,e){let n=zt.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"}),zt.set(e,n));let s=Object.fromEntries(n.formatToParts(new Date(t)).map(r=>[r.type,r.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 Kt=(t,e)=>!t||(t.from===void 0||t.from<=e)&&(t.until===void 0||e<t.until);var le=class{constructor(e,n,s,r,o){h(this,"renderer",e);h(this,"wrap",r);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 sr=Object.freeze,rr=Object.values,Zt=Reflect.apply;function Ce(t){if(t===null||typeof t!="object")return t;for(let e of Zt(rr,Object,[t]))Ce(e);return Zt(sr,Object,[t])}var ot=class extends Error{constructor(n,s){super(s);h(this,"code",n);this.name="MediaError"}};function at(t){return t instanceof ot?{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 Jt=t=>t.blob.contentType.startsWith("video/");var lt="position:absolute;inset:0;width:100%;height:100%;object-fit:contain",ir=1,Re=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((r,o)=>{let i=this.shown;if(i?.mediaId===r)for(let a of this.endListeners)a(i.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(r){return Promise.reject(r)}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,r=this.options.media,o=s?Xt(e):void 0;if(n.kind==="layout"&&o===void 0){await _(()=>{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 i=Promise.resolve();return await _(()=>{this.shown===n&&(n.el.style.visibility="",s?.el.remove(),r&&this.paint("transparent"),r&&s?.mediaId&&(i=r.show([],[s.mediaId],Date.now())))}),await i.catch(()=>{}),s&&this.dispose(s),this.releaseHeld(),"cut"}if(o!==void 0&&r&&n.kind==="native"&&s.kind==="native"){if(n.fadesIn&&!n.queued){let i=Promise.resolve();return await _(()=>{n.el.style.visibility="",this.container.insertBefore(n.el,s.el),i=r.show([n.mediaId],[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await i.catch(()=>{}),or(o).then(async()=>{await r.show([],[s.mediaId],Date.now()).catch(()=>{}),s.el.remove(),this.dispose(s)}),"crossfade"}}else if(o!==void 0&&r&&n.kind==="native"){let i=Promise.resolve();return await _(()=>{n.el.style.visibility="",this.container.insertBefore(n.el,s.el),i=r.show([n.mediaId],[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await i.catch(()=>{}),this.paint("transparent"),Gt(s.el,1,0,o).then(()=>{s.el.remove(),this.dispose(s)}),"crossfade"}else if(o!==void 0)return await _(()=>{n.el.style.visibility="",n.el.style.opacity="0",this.container.append(n.el),n.kind==="video"&&dt(n.el),this.shown=n,this.shownHash=e.blob.hash}),n.view&&(this.held=[],n.view.start().then(()=>this.releaseHeld())),Gt(n.el,0,1,o).then(async()=>{s.el.remove(),this.shown===n&&this.paint(r?"#000":""),r&&s.mediaId&&await r.show([],[s.mediaId],Date.now()).catch(()=>{}),this.dispose(s)}),"crossfade";if(r&&n.kind==="native"){let i=Promise.resolve();await _(()=>{n.el.style.visibility="",s?this.container.insertBefore(n.el,s.el):this.container.prepend(n.el),i=r.show([n.mediaId],s?.mediaId?[s.mediaId]:[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await i.catch(()=>{}),await _(()=>{this.shown===n&&(s?.el.remove(),this.paint("transparent"))})}else{let i=Promise.resolve();await _(()=>{n.el.style.visibility="",n.el.parentNode!==this.container&&this.container.append(n.el),s?.el.remove(),this.paint(r?"#000":""),r&&s?.mediaId&&(i=r.show([],[s.mediaId],Date.now())),n.kind==="video"&&dt(n.el),this.shown=n,this.shownHash=e.blob.hash}),await i.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"?dt(e.el):e?.mediaId&&this.options.media?.control(e.mediaId,"play").catch(()=>{})}make(e){if(e.layout){let{doc:i,source:a,untilPass:d}=e.layout,p=Y(H("div"));p.dataset.blob=e.blob.hash,p.style.cssText=`${lt};visibility:hidden`,this.container.append(p);let c={slide:e,kind:"layout",el:p,ready:Promise.resolve()},l=g=>this.options.path?`${this.options.path}/${g}`:g,m=String(++this.drawn);return c.view=new G(i,a,p,{media:this.options.media,path:this.options.path,endsWith:d?l(d):void 0,onItem:g=>{let w={...g,slide:g.slide?`${m}.${g.slide}`:m};this.held?this.held.push(w):this.item(w)},onPassEnd:g=>{if(d&&g===l(d)&&this.shown===c){for(let w of this.endListeners)w(e);return"hold"}return this.options.onPassEnd?.(g)},onApp:g=>this.app(g)}),c.ready=c.view.ready,c}if(!Jt(e)){if(!e.bytes&&!e.url)throw new Error(`no bytes for ${e.blob.hash}`);let i=e.bytes?URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})):void 0,a=Y(H("img"));return a.src=i??e.url,a.alt="",a.dataset.blob=e.blob.hash,a.style.cssText=this.fill(),{slide:e,kind:"image",el:a,objectUrl:i,ready:a.decode()}}let n=this.options.media;if(n){let i=`m-${ir++}`,a=Y(H("div"));a.dataset.blob=e.blob.hash,a.dataset.media=i,a.style.cssText=`${lt};visibility:hidden`;let d=this.container.getBoundingClientRect(),p={x:d.left,y:d.top,w:d.width||window.innerWidth,h:d.height||window.innerHeight},c=Xt(e),l=c!==void 0&&this.shown?.kind==="native"&&n.capability.animates&&n.capability.layers>=2,m=l?[{property:"opacity",keyframes:[{t:0,v:0},{t:c,v:1}]}]:[],g={slide:e,kind:"native",el:a,mediaId:i,fadesIn:l,ready:Promise.resolve()},w=this.options.fit??"contain";return g.ready=n.prepare({mediaId:i,blob:e.blob.hash,contentType:e.blob.contentType,box:p,z:l?1:0,loop:e.loop,muted:!1,fit:w,...l?{tracks:m}:{}}).then(f=>{f.queued&&(g.queued=!0)}),g}let s=e.url?void 0:URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})),r=Y(H("video"));r.dataset.blob=e.blob.hash,r.style.cssText=`${this.fill()};visibility:hidden`,r.muted=!1,r.playsInline=!0,r.preload="auto",r.loop=e.loop,r.src=e.url??s,r.addEventListener("ended",()=>{if(this.shown?.el===r)for(let i of this.endListeners)i(e)}),r.addEventListener("error",()=>{if(this.shown?.el===r)for(let i of this.endListeners)i(e,`video: ${r.error?.message??"error"}`)});let o=new Promise((i,a)=>{if(r.readyState>=2)return i();r.addEventListener("loadeddata",()=>i(),{once:!0}),r.addEventListener("error",()=>a(new Error(`video: ${r.error?.message??"cannot load"}`)),{once:!0})});return this.container.append(r),{slide:e,kind:"video",el:r,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 lt.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 Xt(t){if(t.transition?.type==="crossfade")return Math.max(0,Math.min(t.transition.durationMs??1e3,t.durationMs/2))}var or=t=>new Promise(e=>setTimeout(e,t));function Gt(t,e,n,s){return new Promise(r=>{let o=Date.now(),i=!1,a=()=>{i||(i=!0,t.style.opacity=n===1?"":String(n),r())};t.style.opacity=String(e);let d=()=>{if(i)return;let p=Math.min(1,(Date.now()-o)/s);if(p>=1)return a();t.style.opacity=String(e+(n-e)*p),requestAnimationFrame(d)};requestAnimationFrame(d),setTimeout(a,s+250)})}function dt(t){t.play().catch(()=>(t.muted=!0,t.play())).catch(()=>{})}function _(t){return new Promise((e,n)=>{let s=!1,r=()=>{if(!s){s=!0;try{t(),e()}catch(o){n(o)}}};requestAnimationFrame(r),setTimeout(r,250)})}var de={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 Qt=Symbol.for("wyndlass.app.attach");var U=class extends Error{constructor(n,s){super(`the app could not run (${n})`);h(this,"reason",n);h(this,"error",s)}};function Z(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 pe(t){let e=t?.id;if(typeof e!="string"||e==="")return!0;for(let n=0;n<en.length;n++)if(e[n]!==en[n])return!1;return!0}var en="com.wyndlass.";function lr(t=typeof navigator>"u"?"":navigator.userAgent){let e=/Chrom(?:e|ium)\/(\d+)/.exec(t);return e?Number(e[1]):void 0}function tn(t,e=de.apiVersion,n=lr()){return!(t?.playerApi!==void 0&&t.playerApi>e||t?.webView!==void 0&&n!==void 0&&t.webView>n)}function dr(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 pt=new Map;function pr(){let t=Ht(8),e="";for(let n=0;n<8;n++)e+=cr[t[n]%26];return e}var cr="abcdefghijklmnopqrstuvwxyz",ur=0;function nn(t,e,n){let s=pt.get(t);if(!s){let r=Ke.make([e],"text/javascript");s=import(r).then(o=>hr(o,n),o=>{throw o instanceof U?o:new U("load",Z(o))}).finally(()=>Ke.revoke(r)),pt.set(t,s),s.catch(()=>pt.delete(t))}return s}function hr(t,e){let n=t.default;if(typeof n=="function"&&n.prototype instanceof HTMLElement){let s=n.prototype[Qt];if(typeof s!="function")throw new U("shape");let r=class extends n{},o=`${e?.element??"wl-app"}-${pr()}${++ur}`;try{Je.define(o,r)}catch(i){throw new U("shape",Z(i))}return{tag:o,shape:"class",attach:(i,a)=>{Ft(s,i,[a])}}}if(pe(e)&&typeof t.element=="string"&&Je.get(t.element))return{tag:t.element,shape:"legacy"};throw new U("shape")}var mr=3,fr=6e4,Ne=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:dr(this.element)}hook(e,n){if(this.halted)return Promise.resolve();let s;try{s=this.element[e]?.(n)}catch(r){return this.fault(r),Promise.resolve()}return Promise.resolve(s).then(()=>{},r=>this.fault(r))}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<fr),this.faults.push(n),this.faults.length>=mr&&this.stop(Z(e))}},gr=24*3600*1e3,Oe=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(),r=`${n.replace(/\/$/,"")}${e}`;return s?`${r}${r.includes("?")?"&":"?"}${s}`:r}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(r=>`${encodeURIComponent(r)}=${encodeURIComponent(String(n[r]))}`).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 r=JSON.parse(s);return this.dress(r,Date.parse(r.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:r}=t.keyOf(e,n),o=this.inflight.get(r);if(o)return o;let i=this.o.fetch??fetch,a=(async()=>{let d=this.stored(r),p=d?JSON.parse(d):void 0;if(p&&Date.parse(p.expiresAt)>this.now())return this.dress(p,!1);let c=this.url(`/v0/${encodeURIComponent(e)}?${s}`);try{if(!c)throw new Error("no data service");let l=await i(c);if(!l.ok)throw new Error(`the data service answered ${l.status}`);let m=await l.text();return this.keep(r,m),this.dress(JSON.parse(m),!1)}catch(l){if(p&&Date.parse(p.expiresAt)+gr>this.now())return this.dress(p,!0);throw l}})().finally(()=>this.inflight.delete(r));return this.inflight.set(r,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(r=>{if(!r.ok)throw new Error(`the image answered ${r.status}`);return r.blob()}):Promise.reject(new Error("no data service")),this.pictures.set(e,n),n.catch(()=>this.pictures.delete(e))}return n}};var sn="wyndlass.frame";function ln(t,e){return!!t&&(t.apps?t.apps(e):!pe(e))}var yr=new Set(["load","shape","unsupported","error","late","overran"]),rn=50,on=250,ct=class{constructor(e,n,s,r){h(this,"frame",e);h(this,"port",n);h(this,"o",r);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 r=this.told;if(r&&(r.running?Math.abs(e-(r.t+(n-r.at)))<=rn:e===r.t)){r.running&&this.watch();return}let o=!!s&&n-s.at<on&&e>s.t&&Math.abs(e-s.t-(n-s.at))<=rn;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"})}snapshots(e){this.halted||this.post({type:"snapshots",snapshots:e})}hook(e,n){if(this.halted)return Promise.resolve();let s=++this.next;return new Promise(r=>{this.pending.set(s,r),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(){this.o.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=pn(e.paging):e.type==="hooked"&&typeof e.id=="number"?(this.pending.get(e.id)?.(),this.pending.delete(e.id)):e.type==="stopped"&&this.stop(cn(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}))},on)}};async function dn(t,e,n,s,r,o,i,a,d,p){let c=t.appInfo??n.info,l;try{l=new URL(d.frames).origin}catch{return p("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 g=new Promise(A=>{m.addEventListener("load",()=>A(!0),{once:!0}),setTimeout(()=>A(!1),o)});m.src=`${l}/`,e.append(m);let w=(A,k)=>(m.remove(),p(A,k));if(!await g||!m.contentWindow)return w("load");let{port1:f,port2:b}=new MessageChannel,v={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:r.now(),running:!1,startedAt:r.startedAt()},fonts:vr(t.props??{},d),snapshots:an(t,s),deadlineMs:o};return new Promise(A=>{let k,I=!1,O=ne=>{I||(I=!0,clearTimeout(V),A(ne))},V=setTimeout(()=>{k?(O({app:k,late:!0}),k.update(r.now())):(f.close(),O(w("load")))},o+1e3),F=!1;f.onmessage=ne=>{let P=ne.data;if(!(!P||typeof P!="object")){if(P.type==="ask"){F&&br(P,s,f);return}if(k){P.type==="ready"?(O({app:k,late:P.late===!0}),k.update(r.now())):k.heard(P);return}if(P.type==="hello"&&!F)F=!0,f.postMessage(v,[v.bundle.bytes.buffer]);else if(P.type==="made"&&Array.isArray(P.hooks)){let se=un(t),be,M=new ct(m,f,{type:"made",hooks:P.hooks.filter(T=>T==="enter"||T==="exit"),paging:pn(P.paging)},{onStop:a,dispose:()=>be?.()});be=s.onSnapshots?.(T=>{T.some(B=>se.has(B))&&M.snapshots(an(t,s))}),k=M,i(k)}else P.type==="failed"&&(f.close(),O(w(yr.has(P.reason)?P.reason:"error",cn(P.error))))}},m.contentWindow.postMessage({type:sn},"*",[b])})}async function br(t,e,n){if(typeof t.id!="number")return;let s=(r,o=[])=>{try{n.postMessage(r,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,wr(t.params))});else if(t.what==="image"&&typeof t.path=="string"){let r=await e.data.imageBlob(t.path),o=new Uint8Array(await r.arrayBuffer());s({type:"answer",id:t.id,value:{bytes:o,type:r.type}},[o.buffer])}else throw new TypeError("not a question")}catch(r){s({type:"answer",id:t.id,error:Z(r)??"Error"})}}function wr(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 pn(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 cn(t){return typeof t=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(t)?t:void 0}function vr(t,e){let n=new Set;for(let r of Object.values(t)){let o=r?.family;r&&typeof r=="object"&&typeof o=="string"&&n.add(o)}let s=[];for(let r of e.fonts??[]){let o=n.has(r.family)?e.font?.(r.blob):void 0;o&&s.push({family:r.family,weight:r.weight,style:r.style,blob:r.blob,bytes:o.slice()})}return s}function un(t){let e=new Set;t.binding?.table&&e.add(t.binding.table);for(let n of Object.values(t.props??{}))typeof n=="string"&&/^tbl_[0-9a-z]{26}$/.test(n)&&e.add(n);return e}function an(t,e){let n={};for(let s of un(t)){let r=e.snapshot?.(s);r&&(n[s]=r)}return n}function hn(t){let e=ie?.setStyle??((n,s,r,o)=>n.style.setProperty(s,r,o));e(t,"contain","strict","important"),e(t,"overflow","clip","important")}var mn=t=>!!(t?.filter?.length||t?.sort||t?.limit),kr=t=>t==null||t==="";function xr(t,e){if(!kr(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 Mr(t,e){let n=Array.from(t),s=Array.from(e);for(let r=0;r<n.length&&r<s.length;r++){let o=n[r].codePointAt(0)-s[r].codePointAt(0);if(o!==0)return o}return n.length-s.length}function Ar(t,e,n,s,r){let o=t,i=Array.isArray(o?.rows)?o.rows:[],a=Array.isArray(o?.columns)?o.columns:void 0,d=p=>a?.find(c=>c.key===p)?.type??"text";for(let p of e.filter??[]){if(a&&!a.some(m=>m.key===p.column))return[];let c=p.value;if(c!==null&&typeof c=="object"){let m=c;if(c=Le(n,s,m.screen,m.key),c===void 0)return[]}let l=d(p.column);i=i.filter(m=>Vt(m[p.column],p.op,c,l,r,s))}if(e.sort){let{column:p}=e.sort,c=d(p),l=e.sort.order==="desc"?-1:1;i=i.map((m,g)=>({row:m,i:g,key:xr(m[p],c)})).sort((m,g)=>{if(m.key===void 0||g.key===void 0)return m.key===void 0&&g.key===void 0?m.i-g.i:m.key===void 0?1:-1;let w=typeof m.key=="number"&&typeof g.key=="number"?m.key-g.key:Mr(String(m.key),String(g.key));return w===0?m.i-g.i:w<0?-l:l}).map(m=>m.row)}return e.limit?i.slice(0,e.limit):i}function fn(t,e,n,s,r){return!t||typeof t!="object"?t:{...t,rows:Ar(t,e,n,s,r)}}function Sr(t){if(!t)return"#000000";if("value"in t)return t.value;let e=t.gradient,n=[...e.stops].sort((s,r)=>s.at-r.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 yn(t,e,n,s=[]){let r=H("div");if(r.dataset.background="",r.style.cssText="position:absolute;inset:0;pointer-events:none",r.style.background=Sr(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(""),i=`<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(i)}")`;r.dataset.holes=String(s.length);for(let[d,p]of[["maskImage",a],["maskSize","100% 100%"],["maskRepeat","no-repeat"]])r.style[d]=p,r.style[`webkit${d[0].toUpperCase()}${d.slice(1)}`]=p}return t.prepend(r),r}function bn(t,e=t.frame){let n=H("div");return n.dataset.element=t.id,n.style.cssText="position:absolute;box-sizing:border-box",Er(n,e),n}function Er(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 wn(t,e,n){if(e.type==="text"){let s=e.style??{};t.textContent=ut(e,ce(n)),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"&&Tr(e)?(t.style.opacity=e.opacity!==void 0?String(e.opacity):"",t.append(Pr(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 ut(t,e){return t.bind?tt(t.bind,e):nt(t.text??"",e)}function ce(t){return{screen:t?.screen??null,timeZone:t?.timeZone,tables:new Proxy({},{get:(e,n)=>typeof n=="string"?t?.snapshot?.(n):void 0})}}var Tr=t=>t.shape==="line"||t.shape==="arrow"||t.shape==="polygon";function Pr(t){let e=Se("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,r=t.stroke?.width??(t.shape==="polygon"?0:4),i=(t.points?.length?t.points:t.shape==="polygon"?[[.5,0],[1,1],[0,1]]:[[0,.5],[1,.5]]).map(([c,l])=>[c*n,l*s]),a=t.stroke?.color?.value??"#000000",d=t.stroke?.dash==="dashed"?`${r*3} ${r*2}`:t.stroke?.dash==="dotted"?`0 ${r*2}`:"",p=Se(t.shape==="polygon"?"polygon":"polyline");if(p.setAttribute("points",i.map(c=>`${c[0]},${c[1]}`).join(" ")),p.setAttribute("fill",t.shape==="polygon"?t.fill?.value??"none":"none"),r&&(p.setAttribute("stroke",a),p.setAttribute("stroke-width",String(r)),p.setAttribute("stroke-linecap",t.stroke?.dash==="dotted"?"round":"butt"),p.setAttribute("stroke-linejoin","round"),d&&p.setAttribute("stroke-dasharray",d)),e.append(p),t.shape==="arrow"&&i.length>=2){let[c,l]=[i[i.length-2],i[i.length-1]],m=Math.atan2(l[1]-c[1],l[0]-c[0]),g=Math.max(10,r*3.5),w=b=>`${l[0]-g*Math.cos(m)+b*g*.5*Math.sin(m)},${l[1]-g*Math.sin(m)-b*g*.5*Math.cos(m)}`;p.setAttribute("points",[...i.slice(0,-1),[l[0]-g*.8*Math.cos(m),l[1]-g*.8*Math.sin(m)]].map(b=>`${b[0]},${b[1]}`).join(" "));let f=Se("polygon");f.setAttribute("points",`${l[0]},${l[1]} ${w(1)} ${w(-1)}`),f.setAttribute("fill",a),e.append(f)}return e}async function vn(t,e,n,s,r,o,i,a=()=>{},d){let p=t.appInfo??n.info;hn(e),p?.id&&(e.dataset.app=p.id);let c=(M,T)=>(e.dataset.appFailed=M,{failed:M,...T?{error:T}:{}});if(!tn(p?.requires))return c("unsupported");let l=s.appHost;if(l&&!l.custom&&!pe(p))return c("unsupported");if(ln(d,p))return dn(t,e,n,s,r,o,i,a,d,c);let m=t.app;if(l?.held?.(m))return e.dataset.appHeld="",{held:!0};await l?.mounting?.(m,p?.id);let g=!1,w=()=>{g||(g=!0,l?.unmounted?.(m))},f;try{f=await nn(n.hash,n.bytes,p)}catch(M){return w(),M instanceof U?c(M.reason,M.error):c("load")}let b=f.tag,v=p?.id?s.controllerFor?.({id:p.id,instance:t.id,capability:p.player??"read"}):void 0,A,k=new Promise(M=>{A=M}),I,O=new Set,V={app:p?.id??t.app.id,element:b,props:st(t.props??{},ce(s)),place:s.place,screen:s.screen??null,timeZone:s.timeZone??Intl.DateTimeFormat().resolvedOptions().timeZone,locale:s.locale??navigator.language,preview:!!s.preview,now:r.now,startedAt:r.startedAt,ready:()=>A(),data:(M,T)=>s.data?s.data.get(M,T):Promise.reject(new Error("no data service")),kept:(M,T)=>s.data?.kept?.(M,T),image:M=>s.data?s.data.image(M):Promise.reject(new Error("no data service")),snapshot:M=>{let T=s.snapshot?.(M)??null;if(!T||t.binding?.table!==M||!mn(t.binding))return T;let B=fn(T,t.binding,s.screen,V.timeZone,r.now());return I?.from===T&&I.rows.length===B.rows.length&&I.rows.every((ns,ss)=>ns===B.rows[ss])?I.snapshot:(I={from:T,rows:B.rows,snapshot:B},B)},...t.binding?{binding:t.binding}:{},...s.onSnapshots?{onSnapshot:M=>(O.add(M),()=>{O.delete(M)})}:{},...t.sensors?.includes("impressions")&&s.impressions?.measuring?{impressions:{now:()=>s.impressions.now(),on:M=>s.impressions.on(M)}}:{},...v?{controller:v}:{}},F;try{F=H(b),f.shape==="class"?f.attach(F,V):F.wyndlass=V}catch(M){return v?.dispose(),w(),c("error",Z(M))}F.style.cssText="display:block;width:100%;height:100%",l?.contained?.(e,F);let ne=Object.values(t.props??{}).some(M=>typeof M=="string"&&Ie(M)),P=s.onSnapshots?.(M=>{ne&&(V.props=st(t.props??{},ce(s)));for(let T of M)for(let B of[...O])try{B(T)}catch{}}),se=new Ne(F,{onStop:a,dispose:()=>{P?.(),O.clear(),v?.dispose(),w()}});e.append(F),i(se);let be=await Promise.race([k.then(()=>!1),new Promise(M=>setTimeout(()=>M(!0),o))]);return se.update(V.now()),{app:se,late:be}}var gn=new Set;async function kn(t,e){for(let n of t){let s=`${n.family}/${n.weight??400}/${n.style??"normal"}/${n.blob}`,r=e(n.blob);if(!(gn.has(s)||!r||typeof FontFace>"u"))try{let o=new FontFace(n.family,r,{weight:String(n.weight??400),style:n.style??"normal"});document.fonts.add(await o.load()),gn.add(s)}catch{}}}function ue(t){let e=[],n=null;for(let s of t){let r=s.start,o=Math.max(0,r.kind==="at"?r.ms:r.kind==="withPrevious"?(n?.startMs??0)+(r.offsetMs??0):(n?.endMs??0)+(r.offsetMs??0));n={startMs:o,endMs:o+s.durationMs},e.push(n)}return{clips:e,autoDurationMs:e.reduce((s,r)=>Math.max(s,r.endMs),0)}}var Lr=new Set(["x","y","scaleX","scaleY","rotation","opacity","reveal"]);function Ir(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 r=t.slice(0,-1).filter(o=>o.type==="shape"&&o.clip!==!1);if(r.some(o=>o.shape!=="rectangle"||(o.radius??0)>0))return s("clipped-to-shape");if(r.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(i=>Lr.has(i))))return s("animated")}return{path:"native"}}var Cr={fade:["opacity"],slide:["x","opacity"],scale:["scaleX","scaleY","opacity"],pulse:["scaleX","scaleY"],loop:["scaleX","scaleY"],wipe:["reveal"]};function xn(t,e,n,s){let r=e&&t.overrides?.[e]||{},o=new Map;for(let l of t.animation?.clips??[]){let m=[...l.preset?Cr[l.preset]??[]:[],...(l.lanes??[]).map(g=>g.property)];o.set(l.element,[...o.get(l.element)??[],...m])}let i=l=>l.type==="library"&&!!l.source&&!!n[l.source.item.id]?.video,a=l=>!i(l)&&!(l.type==="shape"&&!l.fill&&!(l.stroke?.width??0)),d=[],p=(l,m,g,w)=>{for(let f of l){let b=r[f.id];if(b?.hidden??f.hidden??!1)continue;let v=b?.frame??f.frame,A={x:m.x+v.x,y:m.y+v.y,w:v.w,h:v.h},k={type:f.type==="shape"?"shape":"library",rotation:v.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:A,chain:[...g,k],ancestors:w}),f.type==="shape"&&p(f.children??[],A,[...g,k],[...w,f])}};p(t.elements,{x:0,y:0},[],[]);let c=(l,m)=>l.x<m.x+m.w&&m.x<l.x+l.w&&l.y<m.y+m.h&&m.y<l.y+l.h;return d.flatMap((l,m)=>{if(!i(l.e))return[];let g=d.slice(0,m).some(w=>a(w.e)&&c(w.box,l.box));return[{element:l.e.id,path:Ir(l.chain,{pageBelow:g},s),box:l.box}]})}var Rr=[0,0,1,1];function ht(t,e){if(e<=0)return 0;if(e>=1)return 1;let[n,s,r,o]=t;if(n===s&&r===o)return e;let i=3*n,a=3*(r-n)-i,d=1-i-a,p=3*s,c=3*(o-s)-p,l=1-p-c,m=k=>((d*k+a)*k+i)*k,g=k=>((l*k+c)*k+p)*k,w=k=>(3*d*k+2*a)*k+i,f=1e-7,b=e;for(let k=0;k<8;k++){let I=m(b)-e;if(Math.abs(I)<f)return g(b);let O=w(b);if(Math.abs(O)<1e-6)break;b-=I/O}let v=0,A=1;for(b=e;v<A;){let k=m(b);if(Math.abs(k-e)<f||(e>k?v=b:A=b,b=(A-v)/2+v,A-v<f))break}return g(b)}function $e(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 r=0;for(;e>=n[r+1].t;)r++;let o=n[r],i=n[r+1];return o.v+(i.v-o.v)*ht(o.easing??Rr,(e-o.t)/(i.t-o.t))}function Mn(t,e,n){let s={x:t.x,y:t.y,scaleX:1,scaleY:1,rotation:0,opacity:1};for(let r of e??[])s[r.property]=$e(r,n);return s}var he={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 An(t){if(!t)return he.ease;if(he[t])return he[t];let n=/^cubic-bezier\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)$/.exec(t)?.slice(1).map(Number);return n&&n.every(Number.isFinite)?n:he.ease}function Nr(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 Tn={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})},Ta=Object.keys(Tn);var Or={x:t=>t.x,y:t=>t.y,scaleX:()=>1,scaleY:()=>1,rotation:()=>0,opacity:()=>1,reveal:()=>1,chars:()=>1,count:()=>1},Sn=1e3,$r=he["ease-in-out"];function Pn(t,e){let n=ue(t).clips,s=new Map,r=new Set,o=a=>{let d=s.get(a);return d||s.set(a,d={keys:new Map,colour:null,loops:[],tickers:[]}),d},i=(a,d,p)=>{let c=o(a).keys,l=c.get(d)??[];c.set(d,l),l.push(p)};t.forEach((a,d)=>{let p=e.get(a.element);if(!p)return;let{startMs:c,endMs:l}=n[d],m=An(a.easing);if(a.lanes?.length){for(let f of a.lanes)for(let b of f.keyframes){let v={t:c+b.at,easing:An(b.easing??a.easing)};if(f.property==="color"){let A=Nr(b.value);if(!A)continue;let k=o(a.element);k.colour??(k.colour=[[],[],[],[]]),A.forEach((I,O)=>k.colour[O].push({...v,v:I}))}else i(a.element,f.property,{...v,v:Number(b.value)})}return}let g=a.preset??"fade";if(a.kind==="emphasis"){g==="pulse"?(i(a.element,"pulse",{t:c,v:1,easing:m}),i(a.element,"pulse",{t:(c+l)/2,v:1.05,easing:m}),i(a.element,"pulse",{t:l,v:1})):g==="loop"?o(a.element).loops.push({startMs:c,endMs:l}):g==="ticker"?o(a.element).tickers.push({startMs:c,endMs:l}):r.add(g);return}let w=Tn[g];if(!w){r.add(g);return}for(let[f,b]of Object.entries(w(p))){let v=Or[f](p),[A,k]=a.kind==="enter"?[b,v]:[v,b];i(a.element,f,{t:c,v:A,easing:m}),i(a.element,f,{t:l,v:k})}});for(let a of s.values()){for(let d of a.keys.values())d.sort((p,c)=>p.t-c.t);for(let d of a.colour??[])d.sort((p,c)=>p.t-c.t)}return{motions:s,unplayed:[...r].sort()}}var Dr=["x","y","scaleX","scaleY","rotation","opacity"];function Ln(t,e,n,s=!1){if(s)return jr(t,e,n);let r=(d,p)=>{let c=t?.keys.get(d);return c?$e({property:"x",keyframes:c},n):p},o=Mn(e,Dr.flatMap(d=>t?.keys.get(d)?[{property:d,keyframes:t.keys.get(d)}]:[]),n),i=r("pulse",1);for(let d of t?.loops??[])if(n>=d.startMs&&n<d.endMs){let p=(n-d.startMs)%Sn/Sn;i*=1+.05*ht($r,p<.5?p*2:2-p*2)}let a=t?.tickers.find(d=>n>=d.startMs&&n<d.endMs);return{...o,reveal:r("reveal",1),chars:r("chars",1),count:r("count",1),pulse:i,scroll:a?(n-a.startMs)/(a.endMs-a.startMs):null,color:t?.colour?t.colour.map(d=>$e({property:"x",keyframes:d},n)):null}}function En(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 jr(t,e,n){let s=(r,o)=>{let i=t?.keys.get(r);return i?En(i,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(r=>En(r,n)):null}}function Fr(t,e){let n=Array.from(t);return e>=1?t:n.slice(0,Math.max(0,Math.round(e*n.length))).join("")}function Hr(t,e){return e>=1?t:t.replace(/\d+(?:\.\d+)?/g,n=>(Number(n)*Math.max(0,e)).toFixed((n.split(".")[1]??"").length))}function In(t,e,n){if(t<=1||e<=0)return[{page:0,startMs:0,endMs:n}];let s=[];for(let r=0;r*e<n&&r<1e3;r++)s.push({page:r%t,startMs:r*e,endMs:Math.min((r+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 r=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=Hr(Fr(s,n.chars),n.count);if(n.scroll!==null){let i=t.querySelector("[data-ticker]");i||(i=document.createElement("span"),i.dataset.ticker="",i.style.cssText="display:inline-block;white-space:nowrap;will-change:transform",t.replaceChildren(i),t.style.overflow="hidden"),i.textContent!==o&&(i.textContent=o);let a=e.frame.w,d=i.scrollWidth;i.style.transform=`translateX(${a-n.scroll*(a+d)}px)`}else(t.textContent!==o||t.querySelector("[data-ticker]"))&&(t.textContent=o,t.style.overflow="");r&&(t.style.color=r)}else if(e.type==="shape"&&r){let o=t.querySelector("polygon, polyline");o&&e.shape==="polygon"?o.setAttribute("fill",r):o||(t.style.background=r)}}function Br(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 Rn(t,e,n,s){let r=parseInt((await et(Qe(`${t}
2
+ "use strict";(()=>{var as=Object.defineProperty;var ls=(t,e,n)=>e in t?as(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var h=(t,e,n)=>ls(t,typeof e!="symbol"?e+"":e,n);var Ht=Reflect.apply,qe=Object.getOwnPropertyDescriptor,Ae=Object.getPrototypeOf,N=globalThis,x=(t,e,n)=>Ht(t,e,n),D=(t,e)=>t?qe(t,e)?.get:void 0,We=(t,e)=>t?qe(t,e)?.set:void 0,C=t=>N[t]?.prototype,Bt=(t,e,n)=>Ht(t,e,n);var yi=Date.now,bi=new TextEncoder,wi=new TextDecoder,vi=TextEncoder.prototype.encode,ki=TextDecoder.prototype.decode;var xi=D(Ae(Uint8Array.prototype),"length");var ds=Uint8Array,ps=t=>new ds(t),cs=[];for(let t=0;t<256;t++)cs[t]=(t<16?"0":"")+t.toString(16);var us=Object.create(null);for(let t=0;t<64;t++)us["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[t]]=t;var Lt=N.crypto?.subtle,ze=Lt?Ae(Lt):void 0,_e=N.crypto,hs=_e?Ae(_e).getRandomValues:void 0,Mi=ze?.digest,Ai=ze?.importKey,Si=ze?.sign;var Ut=t=>x(hs,_e,[ps(t)]),Ei={setTimeout:N.setTimeout,clearTimeout:N.clearTimeout,setInterval:N.setInterval,clearInterval:N.clearInterval};var Ti=N.fetch;var oe=C("MessagePort"),Pi=oe&&oe.postMessage,Li=We(oe,"onmessage"),Ii=oe&&oe.start,Ve=C("MessageEvent"),Ci=D(Ve,"data"),Ri=D(Ve,"ports"),Ni=D(Ve,"source");var _t=C("EventTarget"),Oi=_t?.addEventListener,$i=_t?.removeEventListener;var Di=N.Worker,ke=C("Worker"),ji=ke&&ke.postMessage,Fi=We(ke,"onmessage"),Hi=We(ke,"onerror");var It=N.Blob,xe=N.URL,Ct=xe?.createObjectURL,ms=xe?.revokeObjectURL,Ke=It&&Ct?{make:(t,e)=>x(Ct,xe,[new It(t,{type:e})]),revoke:t=>x(ms,xe,[t])}:void 0,re=N.customElements,Ze=re?Ae(re):void 0,fs=Ze?.define,gs=Ze?.get,Je=re&&Ze?{define:(t,e)=>x(fs,re,[t,e]),get:t=>x(gs,re,[t])}:void 0,qt=N.document,Me=C("Document"),ys=Me?.createElement,bs=Me?.createElementNS,ws=WeakSet.prototype.add,Bi=WeakSet.prototype.has,Ye=new WeakSet,vs=new WeakSet,Se=(t,e)=>(x(ws,t,[e]),e);function H(t){return Se(Ye,x(ys,qt,[t]))}var Ee=t=>Se(Ye,x(bs,qt,["http://www.w3.org/2000/svg",t]));var X=t=>Se(vs,t);var ae=C("Node"),Xe=C("Element"),Te=C("HTMLElement"),G=t=>D(ae,t),Rt=G("parentNode"),ks=G("firstChild"),xs=G("lastChild"),Ms=G("nextSibling"),As=G("nodeType"),Ss=G("childNodes"),Es=D(Xe,"localName"),Ts=ae?.appendChild,Ps=ae?.insertBefore,Ls=ae?.removeChild,K=Xe,Is=K?.getAttribute,Cs=K?.getAttributeNames,Rs=K?.setAttribute,Ns=K?.removeAttribute,Os=K?.matches,$s=K?.attachShadow,Ds=K?.querySelectorAll,js=C("HTMLDialogElement")?.close,Fs=D(C("NodeList"),"length"),Hs=C("NodeList")?.item,Pe=C("MutationRecord"),Bs=D(Pe,"type"),Us=D(Pe,"target"),_s=D(Pe,"addedNodes"),qs=D(Pe,"removedNodes"),Nt=N.MutationObserver,Ws=C("MutationObserver")?.observe,Wt=Me?qe(Me,"adoptedStyleSheets"):void 0,Ot=Wt?.get,$t=Wt?.set,zs=N.requestAnimationFrame,Dt=D(Te,"style"),Le=C("CSSStyleDeclaration"),Vs=Le?.getPropertyValue,Ks=Le?.getPropertyPriority,Zs=Le?.setProperty,jt=Te?.showPopover,Ft=Te?.hidePopover,Js=D(C("Event"),"target"),Ys=C("Event")?.stopImmediatePropagation,ve=t=>{let e=[];for(let n=0,s=x(Fs,t,[]);n<s;n++)e[n]=x(Hs,t,[n]);return e},ie=Nt&&ae&&Xe&&Te&&Le?{parent:t=>x(Rt,t,[]),first:t=>x(ks,t,[]),last:t=>x(xs,t,[]),next:t=>x(Ms,t,[]),isElement:t=>x(As,t,[])===1,name:t=>x(Es,t,[]),children:t=>ve(x(Ss,t,[])),append:(t,e)=>x(Ts,t,[e]),insert:(t,e,n)=>x(Ps,t,[e,n]),remove:t=>{let e=x(Rt,t,[]);e&&x(Ls,e,[t])},attribute:(t,e)=>x(Is,t,[e]),attributes:t=>x(Cs,t,[]),setAttribute:(t,e,n)=>x(Rs,t,[e,n]),removeAttribute:(t,e)=>x(Ns,t,[e]),matches:(t,e)=>x(Os,t,[e]),all:(t,e)=>ve(x(Ds,t,[e])),closeDialog:t=>x(js,t,[]),shadow:t=>x($s,t,[{mode:"closed"}]),style:(t,e)=>{let n=x(Dt,t,[]);return[x(Vs,n,[e]),x(Ks,n,[e])]},setStyle:(t,e,n,s="")=>x(Zs,x(Dt,t,[]),[e,n,s]),observer:t=>new Nt(t),observe:(t,e,n)=>x(Ws,t,[e,n]),record:t=>({type:x(Bs,t,[]),target:x(Us,t,[]),added:ve(x(_s,t,[])),removed:ve(x(qs,t,[]))}),adopted:Ot&&$t?{count:t=>x(Ot,t,[]).length,clear:t=>x($t,t,[[]])}:void 0,frame:t=>x(zs,globalThis,[t]),popover:jt&&Ft?{show:t=>x(jt,t,[]),hide:t=>x(Ft,t,[])}:void 0,target:t=>x(Js,t,[]),stop:t=>x(Ys,t,[])}:void 0;function Ge(t){if(Se(Ye,t),ie)for(let e of ie.children(t))ie.isElement(e)&&Ge(e);return t}var Xs;function Qe(t,e){try{Xs?.caught(t,e)}catch{}}var et=t=>new TextEncoder().encode(t);function Gs(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 tt(t){return Gs(await crypto.subtle.digest("SHA-256",t))}var Qs=["name","type","tags","location","site","district","region","organization","path","timeZone"],er=["text","date","currency","truncate"],zt={site:"SITE",district:"DISTRICT",region:"REGION",organization:"ORGANIZATION"};function Ie(t,e,n,s){if(!t)return n==="timeZone"&&e?e:void 0;if(n==="fields")return s&&t.fields?.[s]||void 0;let r=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 r.at(-1)?.name;case"path":return r.length?r.map(o=>o.name).join(" \u203A "):void 0;case"timeZone":return e||void 0;default:return zt[n]?[...r].reverse().find(o=>o.kind===zt[n])?.name:void 0}}function tr(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 nt(t,e){return(t.source==="screen"?Ie(e.screen,e.timeZone,t.field,t.key):tr(e.tables?.[t.table]?.rows?.[t.row??0]?.[t.column],t.format))??t.fallback??""}function Vt(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"),r=n.find(o=>!o[1].startsWith('"'))?.[1];return e[1]!==void 0?r?null:e[1].startsWith("fields.")?{source:"screen",field:"fields",key:e[1].slice(7),...s!==void 0?{fallback:s}:{}}:Qs.includes(e[1])?{source:"screen",field:e[1],...s!==void 0?{fallback:s}:{}}:null:r&&!er.includes(r)?null:{source:"table",table:e[2],row:Number(e[3]),column:e[4]??e[5].replace(/\\(.)/g,"$1"),...r?{format:r}:{},...s!==void 0?{fallback:s}:{}}}function st(t,e){return t.replace(/\{{2}([^{}]*)\}{2}/g,(n,s)=>{let r=Vt(s);return r?nt(r,e):n})}function Ce(t){return[...t.matchAll(/\{{2}([^{}]*)\}{2}/g)].some(e=>Vt(e[1])!==null)}function rt(t,e){return Object.fromEntries(Object.entries(t).map(([n,s])=>[n,typeof s=="string"?st(s,e):s]))}var it=t=>t==null||t==="";function Zt(t,e,n,s,r,o){return nr(t,e,n,s,{now:r,timeZone:o})}function nr(t,e,n,s,r){if(n===null&&(e==="eq"||e==="ne"))return e==="eq"?it(t):!it(t);if(it(t)||n===null||n===void 0)return!1;switch(s){case"number":case"currency":{let o=Number(t),i=Number(n);return typeof n!="boolean"&&n!==""&&Number.isFinite(o)&&Number.isFinite(i)&&ot(e,o-i)}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"?sr(r.now,r.timeZone).date:String(n),i=String(t).slice(0,10);return/^\d{4}-\d{2}-\d{2}$/.test(o)&&ot(e,i<o?-1:i>o?1:0)}case"datetime":{let o=Date.parse(String(t)),i=n==="now"?r.now:Date.parse(String(n));return!Number.isNaN(o)&&!Number.isNaN(i)&&ot(e,o-i)}default:{let o=String(t).toLowerCase(),i=String(n).toLowerCase();return e==="eq"?o===i:e==="ne"?o!==i:e==="contains"?o.includes(i):!1}}}function ot(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 Kt=new Map;function sr(t,e){let n=Kt.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"}),Kt.set(e,n));let s=Object.fromEntries(n.formatToParts(new Date(t)).map(r=>[r.type,r.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 Jt=(t,e)=>!t||(t.from===void 0||t.from<=e)&&(t.until===void 0||e<t.until);var le=class{constructor(e,n,s,r,o){h(this,"renderer",e);h(this,"wrap",r);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 ar=Object.freeze,lr=Object.values,Yt=Reflect.apply;function Re(t){if(t===null||typeof t!="object")return t;for(let e of Yt(lr,Object,[t]))Re(e);return Yt(ar,Object,[t])}var at=class extends Error{constructor(n,s){super(s);h(this,"code",n);this.name="MediaError"}};function lt(t){return t instanceof at?{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 Xt=t=>t.blob.contentType.startsWith("video/");var dt="position:absolute;inset:0;width:100%;height:100%;object-fit:contain",dr=1,Ne=class{constructor(e,n={}){h(this,"container",e);h(this,"options",n);h(this,"prepared",new Map);h(this,"shown",null);h(this,"visible",null);h(this,"turns",0);h(this,"latest",null);h(this,"endListeners",new Set);h(this,"itemListeners",new Set);h(this,"appListeners",new Set);h(this,"held",null);h(this,"heldFor",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((r,o)=>{let i=this.shown;if(i?.mediaId===r)for(let a of this.endListeners)a(i.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(r){return e.layout&&Qe(r,"layout"),Promise.reject(r)}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){let n=++this.turns;this.latest=e;let s=()=>n!==this.turns;if(await this.prepare(e),s())return this.latest!==e&&this.release(e),"cut";let r=this.prepared.get(e);if(!r)throw new Error("the slide was released before it was shown");this.prepared.delete(e);let o=this.visible,i=this.options.media,a=o?Qt(e):void 0;if(r.kind==="layout"&&a===void 0){if(!await _(()=>s()?!1:(o?this.container.insertBefore(r.el,o.el):this.container.append(r.el),this.shown=r,this.shownHash=e.blob.hash,!0)))return this.drop(r);this.hold(r),await r.view.start().catch(()=>{});let l=Promise.resolve();return await _(()=>s()?!1:(r.el.style.visibility="",o?.el.remove(),this.visible=r,i&&this.paint("transparent"),i&&o?.mediaId&&(l=i.show([],[o.mediaId],Date.now())),!0))?(await l.catch(()=>{}),o&&this.dispose(o),this.releaseHeld(r),"cut"):this.drop(r)}if(a!==void 0&&i&&r.kind==="native"&&o.kind==="native"){if(r.fadesIn&&!r.queued){let l=Promise.resolve();return await _(()=>s()?!1:(r.el.style.visibility="",this.container.insertBefore(r.el,o.el),l=i.show([r.mediaId],[],Date.now()),this.shown=r,this.visible=r,this.shownHash=e.blob.hash,!0))?(await l.catch(()=>{}),pr(a).then(async()=>{await i.show([],[o.mediaId],Date.now()).catch(()=>{}),o.el.remove(),this.dispose(o)}),"crossfade"):this.drop(r)}}else if(a!==void 0&&i&&r.kind==="native"){let l=Promise.resolve();return await _(()=>s()?!1:(r.el.style.visibility="",this.container.insertBefore(r.el,o.el),l=i.show([r.mediaId],[],Date.now()),this.shown=r,this.visible=r,this.shownHash=e.blob.hash,!0))?(await l.catch(()=>{}),this.paint("transparent"),en(o.el,1,0,a).then(()=>{o.el.remove(),this.dispose(o)}),"crossfade"):this.drop(r)}else if(a!==void 0)return await _(()=>s()?!1:(r.el.style.visibility="",r.el.style.opacity="0",this.container.append(r.el),r.kind==="video"&&pt(r.el),this.shown=r,this.visible=r,this.shownHash=e.blob.hash,!0))?(r.view&&(this.hold(r),r.view.start().then(()=>this.releaseHeld(r))),en(r.el,0,1,a).then(async()=>{o.el.remove(),this.shown===r&&this.paint(i?"#000":""),i&&o.mediaId&&await i.show([],[o.mediaId],Date.now()).catch(()=>{}),this.dispose(o)}),"crossfade"):this.drop(r);if(i&&r.kind==="native"){let l=Promise.resolve();if(!await _(()=>s()?!1:(r.el.style.visibility="",o?this.container.insertBefore(r.el,o.el):this.container.prepend(r.el),l=i.show([r.mediaId],o?.mediaId?[o.mediaId]:[],Date.now()),this.shown=r,this.shownHash=e.blob.hash,!0)))return this.drop(r);if(await l.catch(()=>{}),!await _(()=>s()?!1:(o?.el.remove(),this.visible=r,this.paint("transparent"),!0)))return this.shown===r&&o?.mediaId&&i.show([o.mediaId],[],Date.now()).catch(()=>{}),this.drop(r)}else{let l=Promise.resolve();if(!await _(()=>s()?!1:(r.el.style.visibility="",r.el.parentNode!==this.container&&this.container.append(r.el),o?.el.remove(),this.paint(i?"#000":""),i&&o?.mediaId&&(l=i.show([],[o.mediaId],Date.now())),r.kind==="video"&&pt(r.el),this.shown=r,this.visible=r,this.shownHash=e.blob.hash,!0)))return this.drop(r);await l.catch(()=>{})}return o&&this.dispose(o),"cut"}paint(e){this.options.background?this.options.background(e):this.container.style.background=e}standby(){this.turns++,this.latest=null;let e=this.visible;this.shown=null,this.visible=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"?pt(e.el):e?.mediaId&&this.options.media?.control(e.mediaId,"play").catch(()=>{})}make(e){if(e.layout){let{doc:i,source:a,untilPass:l}=e.layout,p=X(H("div"));p.dataset.blob=e.blob.hash,p.style.cssText=`${dt};visibility:hidden`,this.container.append(p);let c=String(++this.drawn),d={slide:e,kind:"layout",el:p,drawing:c,ready:Promise.resolve()},m=y=>this.options.path?`${this.options.path}/${y}`:y;return d.view=new Q(i,a,p,{media:this.options.media,path:this.options.path,endsWith:l?m(l):void 0,onItem:y=>{if(d.dropped)return;let w={...y,slide:y.slide?`${c}.${y.slide}`:c};this.held?this.held.push(w):this.item(w)},onPassEnd:y=>{if(l&&y===m(l)&&this.shown===d){for(let w of this.endListeners)w(e);return"hold"}return this.options.onPassEnd?.(y)},onApp:y=>this.app(y)}),d.ready=d.view.ready,d}if(!Xt(e)){if(!e.bytes&&!e.url)throw new Error(`no bytes for ${e.blob.hash}`);let i=e.bytes?URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})):void 0,a=X(H("img"));return a.src=i??e.url,a.alt="",a.dataset.blob=e.blob.hash,a.style.cssText=this.fill(),{slide:e,kind:"image",el:a,objectUrl:i,ready:a.decode()}}let n=this.options.media;if(n){let i=`m-${dr++}`,a=X(H("div"));a.dataset.blob=e.blob.hash,a.dataset.media=i,a.style.cssText=`${dt};visibility:hidden`;let l=this.container.getBoundingClientRect(),p={x:l.left,y:l.top,w:l.width||window.innerWidth,h:l.height||window.innerHeight},c=Qt(e),d=c!==void 0&&this.shown?.kind==="native"&&n.capability.animates&&n.capability.layers>=2,m=d?[{property:"opacity",keyframes:[{t:0,v:0},{t:c,v:1}]}]:[],y={slide:e,kind:"native",el:a,mediaId:i,fadesIn:d,ready:Promise.resolve()},w=this.options.fit??"contain";return y.ready=n.prepare({mediaId:i,blob:e.blob.hash,contentType:e.blob.contentType,box:p,z:d?1:0,loop:e.loop,muted:!1,fit:w,...d?{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})),r=X(H("video"));r.dataset.blob=e.blob.hash,r.style.cssText=`${this.fill()};visibility:hidden`,r.muted=!1,r.playsInline=!0,r.preload="auto",r.loop=e.loop,r.src=e.url??s,r.addEventListener("ended",()=>{if(this.shown?.el===r)for(let i of this.endListeners)i(e)}),r.addEventListener("error",()=>{if(this.shown?.el===r)for(let i of this.endListeners)i(e,`video: ${r.error?.message??"error"}`)});let o=new Promise((i,a)=>{if(r.readyState>=2)return i();r.addEventListener("loadeddata",()=>i(),{once:!0}),r.addEventListener("error",()=>a(new Error(`video: ${r.error?.message??"cannot load"}`)),{once:!0})});return this.container.append(r),{slide:e,kind:"video",el:r,objectUrl:s,ready:o}}item(e){this.options.onItem?.(e);for(let n of this.itemListeners)n(e)}hold(e){this.held??(this.held=[]),this.heldFor=e}releaseHeld(e){if(this.heldFor!==e)return;let n=this.held;this.held=null,this.heldFor=null,n?.length&&setTimeout(()=>{for(let s of n)this.item(s)},0)}drop(e){e.dropped=!0;let n=e.drawing;return this.held&&n&&(this.held=this.held.filter(s=>s.slide!==n&&!s.slide?.startsWith(`${n}.`))),this.releaseHeld(e),this.shown===e&&(this.shown=this.visible,this.shownHash=this.visible?.slide.blob.hash??null),this.dispose(e),"cut"}fill(){let e=this.options.fit==="stretch"?"fill":this.options.fit??"contain";return dt.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 Qt(t){if(t.transition?.type==="crossfade")return Math.max(0,Math.min(t.transition.durationMs??1e3,t.durationMs/2))}var pr=t=>new Promise(e=>setTimeout(e,t));function en(t,e,n,s){return new Promise(r=>{let o=Date.now(),i=!1,a=()=>{i||(i=!0,t.style.opacity=n===1?"":String(n),r())};t.style.opacity=String(e);let l=()=>{if(i)return;let p=Math.min(1,(Date.now()-o)/s);if(p>=1)return a();t.style.opacity=String(e+(n-e)*p),requestAnimationFrame(l)};requestAnimationFrame(l),setTimeout(a,s+250)})}function pt(t){t.play().catch(()=>(t.muted=!0,t.play())).catch(()=>{})}function _(t){return new Promise((e,n)=>{let s=!1,r=()=>{if(!s){s=!0;try{e(t())}catch(o){n(o)}}};requestAnimationFrame(r),setTimeout(r,250)})}var de={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 tn=Symbol.for("wyndlass.app.attach");var U=class extends Error{constructor(n,s){super(`the app could not run (${n})`);h(this,"reason",n);h(this,"error",s)}};function Z(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 pe(t){let e=t?.id;if(typeof e!="string"||e==="")return!0;for(let n=0;n<nn.length;n++)if(e[n]!==nn[n])return!1;return!0}var nn="com.wyndlass.";function ur(t=typeof navigator>"u"?"":navigator.userAgent){let e=/Chrom(?:e|ium)\/(\d+)/.exec(t);return e?Number(e[1]):void 0}function sn(t,e=de.apiVersion,n=ur()){return!(t?.playerApi!==void 0&&t.playerApi>e||t?.webView!==void 0&&n!==void 0&&t.webView>n)}function hr(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 ct=new Map;function mr(){let t=Ut(8),e="";for(let n=0;n<8;n++)e+=fr[t[n]%26];return e}var fr="abcdefghijklmnopqrstuvwxyz",gr=0;function rn(t,e,n){let s=ct.get(t);if(!s){let r=Ke.make([e],"text/javascript");s=import(r).then(o=>yr(o,n),o=>{throw o instanceof U?o:new U("load",Z(o))}).finally(()=>Ke.revoke(r)),ct.set(t,s),s.catch(()=>ct.delete(t))}return s}function yr(t,e){let n=t.default;if(typeof n=="function"&&n.prototype instanceof HTMLElement){let s=n.prototype[tn];if(typeof s!="function")throw new U("shape");let r=class extends n{},o=`${e?.element??"wl-app"}-${mr()}${++gr}`;try{Je.define(o,r)}catch(i){throw new U("shape",Z(i))}return{tag:o,shape:"class",attach:(i,a)=>{Bt(s,i,[a])}}}if(pe(e)&&typeof t.element=="string"&&Je.get(t.element))return{tag:t.element,shape:"legacy"};throw new U("shape")}var br=3,wr=6e4,Oe=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:hr(this.element)}hook(e,n){if(this.halted)return Promise.resolve();let s;try{s=this.element[e]?.(n)}catch(r){return this.fault(r),Promise.resolve()}return Promise.resolve(s).then(()=>{},r=>this.fault(r))}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<wr),this.faults.push(n),this.faults.length>=br&&this.stop(Z(e))}},vr=24*3600*1e3,$e=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(),r=`${n.replace(/\/$/,"")}${e}`;return s?`${r}${r.includes("?")?"&":"?"}${s}`:r}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(r=>`${encodeURIComponent(r)}=${encodeURIComponent(String(n[r]))}`).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 r=JSON.parse(s);return this.dress(r,Date.parse(r.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:r}=t.keyOf(e,n),o=this.inflight.get(r);if(o)return o;let i=this.o.fetch??fetch,a=(async()=>{let l=this.stored(r),p=l?JSON.parse(l):void 0;if(p&&Date.parse(p.expiresAt)>this.now())return this.dress(p,!1);let c=this.url(`/v0/${encodeURIComponent(e)}?${s}`);try{if(!c)throw new Error("no data service");let d=await i(c);if(!d.ok)throw new Error(`the data service answered ${d.status}`);let m=await d.text();return this.keep(r,m),this.dress(JSON.parse(m),!1)}catch(d){if(p&&Date.parse(p.expiresAt)+vr>this.now())return this.dress(p,!0);throw d}})().finally(()=>this.inflight.delete(r));return this.inflight.set(r,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(r=>{if(!r.ok)throw new Error(`the image answered ${r.status}`);return r.blob()}):Promise.reject(new Error("no data service")),this.pictures.set(e,n),n.catch(()=>this.pictures.delete(e))}return n}};var on="wyndlass.frame";function pn(t,e){return!!t&&(t.apps?t.apps(e):!pe(e))}var kr=new Set(["load","shape","unsupported","error","late","overran"]),an=50,ln=250,ut=class{constructor(e,n,s,r){h(this,"frame",e);h(this,"port",n);h(this,"o",r);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 r=this.told;if(r&&(r.running?Math.abs(e-(r.t+(n-r.at)))<=an:e===r.t)){r.running&&this.watch();return}let o=!!s&&n-s.at<ln&&e>s.t&&Math.abs(e-s.t-(n-s.at))<=an;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"})}snapshots(e){this.halted||this.post({type:"snapshots",snapshots:e})}hook(e,n){if(this.halted)return Promise.resolve();let s=++this.next;return new Promise(r=>{this.pending.set(s,r),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(){this.o.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=un(e.paging):e.type==="hooked"&&typeof e.id=="number"?(this.pending.get(e.id)?.(),this.pending.delete(e.id)):e.type==="stopped"&&this.stop(hn(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}))},ln)}};async function cn(t,e,n,s,r,o,i,a,l,p){let c=t.appInfo??n.info,d;try{d=new URL(l.frames).origin}catch{return p("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(A=>{m.addEventListener("load",()=>A(!0),{once:!0}),setTimeout(()=>A(!1),o)});m.src=`${d}/`,e.append(m);let w=(A,k)=>(m.remove(),p(A,k));if(!await y||!m.contentWindow)return w("load");let{port1:f,port2:b}=new MessageChannel,v={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:r.now(),running:!1,startedAt:r.startedAt()},fonts:Ar(t.props??{},l),snapshots:dn(t,s),deadlineMs:o};return new Promise(A=>{let k,I=!1,$=ne=>{I||(I=!0,clearTimeout(V),A(ne))},V=setTimeout(()=>{k?($({app:k,late:!0}),k.update(r.now())):(f.close(),$(w("load")))},o+1e3),F=!1;f.onmessage=ne=>{let L=ne.data;if(!(!L||typeof L!="object")){if(L.type==="ask"){F&&xr(L,s,f);return}if(k){L.type==="ready"?($({app:k,late:L.late===!0}),k.update(r.now())):k.heard(L);return}if(L.type==="hello"&&!F)F=!0,f.postMessage(v,[v.bundle.bytes.buffer]);else if(L.type==="made"&&Array.isArray(L.hooks)){let se=mn(t),we,M=new ut(m,f,{type:"made",hooks:L.hooks.filter(P=>P==="enter"||P==="exit"),paging:un(L.paging)},{onStop:a,dispose:()=>we?.()});we=s.onSnapshots?.(P=>{P.some(B=>se.has(B))&&M.snapshots(dn(t,s))}),k=M,i(k)}else L.type==="failed"&&(f.close(),$(w(kr.has(L.reason)?L.reason:"error",hn(L.error))))}},m.contentWindow.postMessage({type:on},"*",[b])})}async function xr(t,e,n){if(typeof t.id!="number")return;let s=(r,o=[])=>{try{n.postMessage(r,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,Mr(t.params))});else if(t.what==="image"&&typeof t.path=="string"){let r=await e.data.imageBlob(t.path),o=new Uint8Array(await r.arrayBuffer());s({type:"answer",id:t.id,value:{bytes:o,type:r.type}},[o.buffer])}else throw new TypeError("not a question")}catch(r){s({type:"answer",id:t.id,error:Z(r)??"Error"})}}function Mr(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 un(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 hn(t){return typeof t=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(t)?t:void 0}function Ar(t,e){let n=new Set;for(let r of Object.values(t)){let o=r?.family;r&&typeof r=="object"&&typeof o=="string"&&n.add(o)}let s=[];for(let r of e.fonts??[]){let o=n.has(r.family)?e.font?.(r.blob):void 0;o&&s.push({family:r.family,weight:r.weight,style:r.style,blob:r.blob,bytes:o.slice()})}return s}function mn(t){let e=new Set;t.binding?.table&&e.add(t.binding.table);for(let n of Object.values(t.props??{}))typeof n=="string"&&/^tbl_[0-9a-z]{26}$/.test(n)&&e.add(n);return e}function dn(t,e){let n={};for(let s of mn(t)){let r=e.snapshot?.(s);r&&(n[s]=r)}return n}function fn(t){let e=ie?.setStyle??((n,s,r,o)=>n.style.setProperty(s,r,o));e(t,"contain","strict","important"),e(t,"overflow","clip","important")}var gn=t=>!!(t?.filter?.length||t?.sort||t?.limit),Sr=t=>t==null||t==="";function Er(t,e){if(!Sr(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 Tr(t,e){let n=Array.from(t),s=Array.from(e);for(let r=0;r<n.length&&r<s.length;r++){let o=n[r].codePointAt(0)-s[r].codePointAt(0);if(o!==0)return o}return n.length-s.length}function Pr(t,e,n,s,r){let o=t,i=Array.isArray(o?.rows)?o.rows:[],a=Array.isArray(o?.columns)?o.columns:void 0,l=p=>a?.find(c=>c.key===p)?.type??"text";for(let p of e.filter??[]){if(a&&!a.some(m=>m.key===p.column))return[];let c=p.value;if(c!==null&&typeof c=="object"){let m=c;if(c=Ie(n,s,m.screen,m.key),c===void 0)return[]}let d=l(p.column);i=i.filter(m=>Zt(m[p.column],p.op,c,d,r,s))}if(e.sort){let{column:p}=e.sort,c=l(p),d=e.sort.order==="desc"?-1:1;i=i.map((m,y)=>({row:m,i:y,key:Er(m[p],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 w=typeof m.key=="number"&&typeof y.key=="number"?m.key-y.key:Tr(String(m.key),String(y.key));return w===0?m.i-y.i:w<0?-d:d}).map(m=>m.row)}return e.limit?i.slice(0,e.limit):i}function yn(t,e,n,s,r){return!t||typeof t!="object"?t:{...t,rows:Pr(t,e,n,s,r)}}function Lr(t){if(!t)return"#000000";if("value"in t)return t.value;let e=t.gradient,n=[...e.stops].sort((s,r)=>s.at-r.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 wn(t,e,n,s=[]){let r=H("div");if(r.dataset.background="",r.style.cssText="position:absolute;inset:0;pointer-events:none",r.style.background=Lr(e),s.length){let o=[`M0 0H${n.width}V${n.height}H0Z`,...s.map(l=>`M${l.x} ${l.y}H${l.x+l.w}V${l.y+l.h}H${l.x}Z`)].join(""),i=`<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(i)}")`;r.dataset.holes=String(s.length);for(let[l,p]of[["maskImage",a],["maskSize","100% 100%"],["maskRepeat","no-repeat"]])r.style[l]=p,r.style[`webkit${l[0].toUpperCase()}${l.slice(1)}`]=p}return t.prepend(r),r}function vn(t,e=t.frame){let n=H("div");return n.dataset.element=t.id,n.style.cssText="position:absolute;box-sizing:border-box",Ir(n,e),n}function Ir(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 kn(t,e,n){if(e.type==="text"){let s=e.style??{};t.textContent=ht(e,ce(n)),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"&&Cr(e)?(t.style.opacity=e.opacity!==void 0?String(e.opacity):"",t.append(Rr(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 ht(t,e){return t.bind?nt(t.bind,e):st(t.text??"",e)}function ce(t){return{screen:t?.screen??null,timeZone:t?.timeZone,tables:new Proxy({},{get:(e,n)=>typeof n=="string"?t?.snapshot?.(n):void 0})}}var Cr=t=>t.shape==="line"||t.shape==="arrow"||t.shape==="polygon";function Rr(t){let e=Ee("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,r=t.stroke?.width??(t.shape==="polygon"?0:4),i=(t.points?.length?t.points:t.shape==="polygon"?[[.5,0],[1,1],[0,1]]:[[0,.5],[1,.5]]).map(([c,d])=>[c*n,d*s]),a=t.stroke?.color?.value??"#000000",l=t.stroke?.dash==="dashed"?`${r*3} ${r*2}`:t.stroke?.dash==="dotted"?`0 ${r*2}`:"",p=Ee(t.shape==="polygon"?"polygon":"polyline");if(p.setAttribute("points",i.map(c=>`${c[0]},${c[1]}`).join(" ")),p.setAttribute("fill",t.shape==="polygon"?t.fill?.value??"none":"none"),r&&(p.setAttribute("stroke",a),p.setAttribute("stroke-width",String(r)),p.setAttribute("stroke-linecap",t.stroke?.dash==="dotted"?"round":"butt"),p.setAttribute("stroke-linejoin","round"),l&&p.setAttribute("stroke-dasharray",l)),e.append(p),t.shape==="arrow"&&i.length>=2){let[c,d]=[i[i.length-2],i[i.length-1]],m=Math.atan2(d[1]-c[1],d[0]-c[0]),y=Math.max(10,r*3.5),w=b=>`${d[0]-y*Math.cos(m)+b*y*.5*Math.sin(m)},${d[1]-y*Math.sin(m)-b*y*.5*Math.cos(m)}`;p.setAttribute("points",[...i.slice(0,-1),[d[0]-y*.8*Math.cos(m),d[1]-y*.8*Math.sin(m)]].map(b=>`${b[0]},${b[1]}`).join(" "));let f=Ee("polygon");f.setAttribute("points",`${d[0]},${d[1]} ${w(1)} ${w(-1)}`),f.setAttribute("fill",a),e.append(f)}return e}async function xn(t,e,n,s,r,o,i,a=()=>{},l){let p=t.appInfo??n.info;fn(e),p?.id&&(e.dataset.app=p.id);let c=(M,P)=>(e.dataset.appFailed=M,{failed:M,...P?{error:P}:{}});if(!sn(p?.requires))return c("unsupported");let d=s.appHost;if(d&&!d.custom&&!pe(p))return c("unsupported");if(pn(l,p))return cn(t,e,n,s,r,o,i,a,l,c);let m=t.app;if(d?.held?.(m))return e.dataset.appHeld="",{held:!0};await d?.mounting?.(m,p?.id);let y=!1,w=()=>{y||(y=!0,d?.unmounted?.(m))},f;try{f=await rn(n.hash,n.bytes,p)}catch(M){return w(),M instanceof U?c(M.reason,M.error):c("load")}let b=f.tag,v=p?.id?s.controllerFor?.({id:p.id,instance:t.id,capability:p.player??"read"}):void 0,A,k=new Promise(M=>{A=M}),I,$=new Set,V={app:p?.id??t.app.id,element:b,props:rt(t.props??{},ce(s)),place:s.place,screen:s.screen??null,timeZone:s.timeZone??Intl.DateTimeFormat().resolvedOptions().timeZone,locale:s.locale??navigator.language,preview:!!s.preview,now:r.now,startedAt:r.startedAt,ready:()=>A(),data:(M,P)=>s.data?s.data.get(M,P):Promise.reject(new Error("no data service")),kept:(M,P)=>s.data?.kept?.(M,P),image:M=>s.data?s.data.image(M):Promise.reject(new Error("no data service")),snapshot:M=>{let P=s.snapshot?.(M)??null;if(!P||t.binding?.table!==M||!gn(t.binding))return P;let B=yn(P,t.binding,s.screen,V.timeZone,r.now());return I?.from===P&&I.rows.length===B.rows.length&&I.rows.every((is,os)=>is===B.rows[os])?I.snapshot:(I={from:P,rows:B.rows,snapshot:B},B)},...t.binding?{binding:t.binding}:{},...s.onSnapshots?{onSnapshot:M=>($.add(M),()=>{$.delete(M)})}:{},...t.sensors?.includes("impressions")&&s.impressions?.measuring?{impressions:{now:()=>s.impressions.now(),on:M=>s.impressions.on(M)}}:{},...v?{controller:v}:{}},F;try{F=H(b),f.shape==="class"?f.attach(F,V):F.wyndlass=V}catch(M){return v?.dispose(),w(),c("error",Z(M))}F.style.cssText="display:block;width:100%;height:100%",d?.contained?.(e,F);let ne=Object.values(t.props??{}).some(M=>typeof M=="string"&&Ce(M)),L=s.onSnapshots?.(M=>{ne&&(V.props=rt(t.props??{},ce(s)));for(let P of M)for(let B of[...$])try{B(P)}catch{}}),se=new Oe(F,{onStop:a,dispose:()=>{L?.(),$.clear(),v?.dispose(),w()}});e.append(F),i(se);let we=await Promise.race([k.then(()=>!1),new Promise(M=>setTimeout(()=>M(!0),o))]);return se.update(V.now()),{app:se,late:we}}var bn=new Set;async function Mn(t,e){for(let n of t){let s=`${n.family}/${n.weight??400}/${n.style??"normal"}/${n.blob}`,r=e(n.blob);if(!(bn.has(s)||!r||typeof FontFace>"u"))try{let o=new FontFace(n.family,r,{weight:String(n.weight??400),style:n.style??"normal"});document.fonts.add(await o.load()),bn.add(s)}catch{}}}function ue(t){let e=[],n=null;for(let s of t){let r=s.start,o=Math.max(0,r.kind==="at"?r.ms:r.kind==="withPrevious"?(n?.startMs??0)+(r.offsetMs??0):(n?.endMs??0)+(r.offsetMs??0));n={startMs:o,endMs:o+s.durationMs},e.push(n)}return{clips:e,autoDurationMs:e.reduce((s,r)=>Math.max(s,r.endMs),0)}}var Nr=new Set(["x","y","scaleX","scaleY","rotation","opacity","reveal"]);function Or(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 r=t.slice(0,-1).filter(o=>o.type==="shape"&&o.clip!==!1);if(r.some(o=>o.shape!=="rectangle"||(o.radius??0)>0))return s("clipped-to-shape");if(r.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(i=>Nr.has(i))))return s("animated")}return{path:"native"}}var $r={fade:["opacity"],slide:["x","opacity"],scale:["scaleX","scaleY","opacity"],pulse:["scaleX","scaleY"],loop:["scaleX","scaleY"],wipe:["reveal"]};function An(t,e,n,s){let r=e&&t.overrides?.[e]||{},o=new Map;for(let d of t.animation?.clips??[]){let m=[...d.preset?$r[d.preset]??[]:[],...(d.lanes??[]).map(y=>y.property)];o.set(d.element,[...o.get(d.element)??[],...m])}let i=d=>d.type==="library"&&!!d.source&&!!n[d.source.item.id]?.video,a=d=>!i(d)&&!(d.type==="shape"&&!d.fill&&!(d.stroke?.width??0)),l=[],p=(d,m,y,w)=>{for(let f of d){let b=r[f.id];if(b?.hidden??f.hidden??!1)continue;let v=b?.frame??f.frame,A={x:m.x+v.x,y:m.y+v.y,w:v.w,h:v.h},k={type:f.type==="shape"?"shape":"library",rotation:v.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}:{}};l.push({e:f,box:A,chain:[...y,k],ancestors:w}),f.type==="shape"&&p(f.children??[],A,[...y,k],[...w,f])}};p(t.elements,{x:0,y:0},[],[]);let c=(d,m)=>d.x<m.x+m.w&&m.x<d.x+d.w&&d.y<m.y+m.h&&m.y<d.y+d.h;return l.flatMap((d,m)=>{if(!i(d.e))return[];let y=l.slice(0,m).some(w=>a(w.e)&&c(w.box,d.box));return[{element:d.e.id,path:Or(d.chain,{pageBelow:y},s),box:d.box}]})}var Dr=[0,0,1,1];function mt(t,e){if(e<=0)return 0;if(e>=1)return 1;let[n,s,r,o]=t;if(n===s&&r===o)return e;let i=3*n,a=3*(r-n)-i,l=1-i-a,p=3*s,c=3*(o-s)-p,d=1-p-c,m=k=>((l*k+a)*k+i)*k,y=k=>((d*k+c)*k+p)*k,w=k=>(3*l*k+2*a)*k+i,f=1e-7,b=e;for(let k=0;k<8;k++){let I=m(b)-e;if(Math.abs(I)<f)return y(b);let $=w(b);if(Math.abs($)<1e-6)break;b-=I/$}let v=0,A=1;for(b=e;v<A;){let k=m(b);if(Math.abs(k-e)<f||(e>k?v=b:A=b,b=(A-v)/2+v,A-v<f))break}return y(b)}function De(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 r=0;for(;e>=n[r+1].t;)r++;let o=n[r],i=n[r+1];return o.v+(i.v-o.v)*mt(o.easing??Dr,(e-o.t)/(i.t-o.t))}function Sn(t,e,n){let s={x:t.x,y:t.y,scaleX:1,scaleY:1,rotation:0,opacity:1};for(let r of e??[])s[r.property]=De(r,n);return s}var he={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 En(t){if(!t)return he.ease;if(he[t])return he[t];let n=/^cubic-bezier\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)$/.exec(t)?.slice(1).map(Number);return n&&n.every(Number.isFinite)?n:he.ease}function jr(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 Ln={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})},Da=Object.keys(Ln);var Fr={x:t=>t.x,y:t=>t.y,scaleX:()=>1,scaleY:()=>1,rotation:()=>0,opacity:()=>1,reveal:()=>1,chars:()=>1,count:()=>1},Tn=1e3,Hr=he["ease-in-out"];function In(t,e){let n=ue(t).clips,s=new Map,r=new Set,o=a=>{let l=s.get(a);return l||s.set(a,l={keys:new Map,colour:null,loops:[],tickers:[]}),l},i=(a,l,p)=>{let c=o(a).keys,d=c.get(l)??[];c.set(l,d),d.push(p)};t.forEach((a,l)=>{let p=e.get(a.element);if(!p)return;let{startMs:c,endMs:d}=n[l],m=En(a.easing);if(a.lanes?.length){for(let f of a.lanes)for(let b of f.keyframes){let v={t:c+b.at,easing:En(b.easing??a.easing)};if(f.property==="color"){let A=jr(b.value);if(!A)continue;let k=o(a.element);k.colour??(k.colour=[[],[],[],[]]),A.forEach((I,$)=>k.colour[$].push({...v,v:I}))}else i(a.element,f.property,{...v,v:Number(b.value)})}return}let y=a.preset??"fade";if(a.kind==="emphasis"){y==="pulse"?(i(a.element,"pulse",{t:c,v:1,easing:m}),i(a.element,"pulse",{t:(c+d)/2,v:1.05,easing:m}),i(a.element,"pulse",{t:d,v:1})):y==="loop"?o(a.element).loops.push({startMs:c,endMs:d}):y==="ticker"?o(a.element).tickers.push({startMs:c,endMs:d}):r.add(y);return}let w=Ln[y];if(!w){r.add(y);return}for(let[f,b]of Object.entries(w(p))){let v=Fr[f](p),[A,k]=a.kind==="enter"?[b,v]:[v,b];i(a.element,f,{t:c,v:A,easing:m}),i(a.element,f,{t:d,v:k})}});for(let a of s.values()){for(let l of a.keys.values())l.sort((p,c)=>p.t-c.t);for(let l of a.colour??[])l.sort((p,c)=>p.t-c.t)}return{motions:s,unplayed:[...r].sort()}}var Br=["x","y","scaleX","scaleY","rotation","opacity"];function Cn(t,e,n,s=!1){if(s)return Ur(t,e,n);let r=(l,p)=>{let c=t?.keys.get(l);return c?De({property:"x",keyframes:c},n):p},o=Sn(e,Br.flatMap(l=>t?.keys.get(l)?[{property:l,keyframes:t.keys.get(l)}]:[]),n),i=r("pulse",1);for(let l of t?.loops??[])if(n>=l.startMs&&n<l.endMs){let p=(n-l.startMs)%Tn/Tn;i*=1+.05*mt(Hr,p<.5?p*2:2-p*2)}let a=t?.tickers.find(l=>n>=l.startMs&&n<l.endMs);return{...o,reveal:r("reveal",1),chars:r("chars",1),count:r("count",1),pulse:i,scroll:a?(n-a.startMs)/(a.endMs-a.startMs):null,color:t?.colour?t.colour.map(l=>De({property:"x",keyframes:l},n)):null}}function Pn(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 Ur(t,e,n){let s=(r,o)=>{let i=t?.keys.get(r);return i?Pn(i,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(r=>Pn(r,n)):null}}function _r(t,e){let n=Array.from(t);return e>=1?t:n.slice(0,Math.max(0,Math.round(e*n.length))).join("")}function qr(t,e){return e>=1?t:t.replace(/\d+(?:\.\d+)?/g,n=>(Number(n)*Math.max(0,e)).toFixed((n.split(".")[1]??"").length))}function Rn(t,e,n){if(t<=1||e<=0)return[{page:0,startMs:0,endMs:n}];let s=[];for(let r=0;r*e<n&&r<1e3;r++)s.push({page:r%t,startMs:r*e,endMs:Math.min((r+1)*e,n)});return s}function Nn(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 r=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=qr(_r(s,n.chars),n.count);if(n.scroll!==null){let i=t.querySelector("[data-ticker]");i||(i=document.createElement("span"),i.dataset.ticker="",i.style.cssText="display:inline-block;white-space:nowrap;will-change:transform",t.replaceChildren(i),t.style.overflow="hidden"),i.textContent!==o&&(i.textContent=o);let a=e.frame.w,l=i.scrollWidth;i.style.transform=`translateX(${a-n.scroll*(a+l)}px)`}else(t.textContent!==o||t.querySelector("[data-ticker]"))&&(t.textContent=o,t.style.overflow="");r&&(t.style.color=r)}else if(e.type==="shape"&&r){let o=t.querySelector("polygon, polyline");o&&e.shape==="polygon"?o.setAttribute("fill",r):o||(t.style.background=r)}}function Wr(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 On(t,e,n,s){let r=parseInt((await tt(et(`${t}
3
3
  ${e}
4
- ${n}`))).slice(0,8),16),o=Br(r),i=Array.from({length:s},(a,d)=>d);for(let a=s-1;a>0;a--){let d=Math.floor(o()*(a+1));[i[a],i[d]]=[i[d],i[a]]}return i}var Ur=250,_r=1e3;function Yt(t,e){let n=t.animation?.duration;if(n?.kind==="fixed")return{durationMs:n.ms};if(n?.kind==="untilPlaylistEnds"){let s=$n(t.elements,n.element);return{durationMs:(s?.source?.kind==="playlist"?e.playlist(s.source.item)??[]:s?.source?[{item:s.source.item}]:[]).reduce((i,a)=>i+(e.slide(a,{imageDurationMs:s?.playback?.imageDurationMs})?.durationMs??0),0)||1e4,untilPass:n.element}}return{durationMs:ue(t.animation?.clips??[]).autoDurationMs}}function On(t,e,n=0){let s={},r=o=>{for(let i of o){if(i.type==="library"&&i.source){let a=i.source.kind==="playlist"?e.playlist(i.source.item)??[]:[{item:i.source.item}];s[i.source.item.id]={video:a.some(d=>{let p=e.slide(d);if(!p)return!1;if(p.blob.contentType.startsWith("video/"))return!0;let c=p.layout;return!!c&&n<4&&Object.values(On(c.doc,c.source,n+1)).some(l=>l.video)})}}i.children&&r(i.children)}};return r(t.elements),s}function $n(t,e){for(let n of t){if(n.id===e)return n;let s=n.children&&$n(n.children,e);if(s)return s}}var G=class{constructor(e,n,s,r={}){h(this,"source",n);h(this,"o",r);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,"bound",[]);h(this,"unheard");h(this,"reduced",typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches);let o=s.clientWidth||window.innerWidth,i=s.clientHeight||window.innerHeight,a=!e.profiles[0].width,d=a?{...e.profiles[0],width:o,height:i}:e.profiles[0];a&&(e={...e,elements:e.elements.map(v=>v.frame.w?v:{...v,frame:{x:0,y:0,w:o,h:i}})});let p=Math.min(o/d.width,i/d.height);this.el=H("div"),this.el.dataset.layout=d.id,this.el.style.cssText=`position:absolute;left:${(o-d.width*p)/2}px;top:${(i-d.height*p)/2}px;width:${d.width}px;height:${d.height}px;transform:scale(${p});transform-origin:0 0;overflow:hidden`,s.append(this.el),this.doc=e;let c=new Map,l=v=>{for(let A of v)c.set(A.id,A.frame),A.children&&l(A.children)};l(e.elements);let m=Pn(e.animation?.clips??[],c);this.motions=m.motions;let g=new Set(m.unplayed.map(v=>`preset ${v}`)),w=[this.loadFonts()],f=r.media?xn(e,void 0,On(e,n),r.media.capability):[];this.native=new Set(f.filter(v=>v.path.path==="native").map(v=>v.element));let b=(v,A)=>{for(let k of v){if(k.hidden)continue;let I=this.element(k,g,w);A.append(I),k.children?.length&&k.type==="shape"&&b(k.children,I)}};b(e.elements,this.el),e.background&&yn(this.el,e.background,d,f.filter(v=>this.native.has(v.element)).map(v=>v.box)),this.unplayed=[...g].sort(),this.ready=Promise.all(w).then(()=>{}),this.bound.length&&(this.unheard=n.onSnapshots?.(()=>this.refill()))}refill(){let e=ce(this.source),n=(this.heldAt??Date.now())-this.startedAt;for(let{el:s,e:r}of this.bound){let o=ut(r,e),i=this.animated.find(a=>a.el===s);i?(i.text=o,this.running||this.place(i,n)):s.textContent!==o&&(s.textContent=o)}}path(e){return this.o.path?`${this.o.path}/${e}`:e}element(e,n,s){let r=bn(e);wn(r,e,this.source),e.type==="text"&&(e.bind||Ie(e.text??""))&&this.bound.push({el:r,e});let o=this.motions.get(e.id);if(o){let i={el:r,e,motion:o,text:e.type==="text"?r.textContent??"":void 0};this.animated.push(i),this.place(i,0)}if(e.type==="library"&&e.source){let i=e.source.kind==="playlist"?this.source.playlist(e.source.item)??[]:[{item:e.source.item}],a=new mt(r,i,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 i=e.app?this.source.app?.(e.app):null;i?s.push(this.mountApp(e,r,i)):n.add(`app ${e.app?.id??e.id}`)}return r}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&&Ge(s)}loadFonts(){return kn(this.doc.fonts??[],e=>this.source.font(e))}async mountApp(e,n,s){let r=this.path(e.id),o=e.app,i=(e.appInfo??s.info)?.id,a={now:()=>this.heldAt??Date.now(),startedAt:()=>this.startedAt||Date.now()},d=(l,m)=>this.o.onApp?.({type:"app.failed",element:r,item:o,...i?{app:i}:{},reason:l,...m?{error:m}:{}}),p=this.o.isolate?{...this.o.isolate,fonts:this.doc.fonts,font:l=>this.source.font(l)}:void 0,c=await vn(e,n,s,this.source,a,this.o.appDeadlineMs??5e3,l=>{this.disposed?l.dispose():this.apps.push({app:l,box:n,path:r,id:e.id,item:o})},l=>d("error",l),p);if(!("held"in c)){if("failed"in c){d(c.failed,c.error);return}c.late&&(this.late.push(r),this.o.onAppLate?.(r,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(),r=s-this.startedAt;for(let o of this.animated)this.place(o,r);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=ue(n).clips,r=typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches;for(let o of this.apps){let a=n.map((f,b)=>({c:f,t:s[b]})).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,p=this.animated.findIndex(f=>f.el===d);p>=0&&(this.animated.splice(p,1),Object.assign(d.style,{opacity:"",transform:"",clipPath:""}));let c=(f,b,v)=>this.hook(o,f.kind,{startAt:b,preset:f.preset??"fade",durationMs:f.durationMs,easing:f.easing,delayMs:0,...f.staggerMs?{staggerMs:f.staggerMs}:{},...v!==void 0?{page:v}:{},reducedMotion:r},d);for(let{c:f,t:b}of a)c(f,e+b.startMs);let l=o.app.paging();if(!l)continue;let m=a.find(f=>f.c.kind==="enter")?.c,g=a.find(f=>f.c.kind==="exit")?.c,w=In(l.count,l.ms,Yt(this.doc,this.source).durationMs);for(let f=1;f<w.length;f++){let b=e+w[f].startMs;g&&c(g,b-g.durationMs,w[f-1].page),m&&c(m,b,w[f].page)}}}hook(e,n,s,r){let o=!1;e.app.hook(n,s).finally(()=>{o=!0});let i=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"&&(r.style.visibility="hidden"))},Math.max(0,i+Ur-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.unheard?.(),this.el.remove()}},Nn=t=>Math.min(Math.max(0,t),2**31-1),mt=class{constructor(e,n,s,r,o,i,a){h(this,"region",e);h(this,"playback",s);h(this,"source",r);h(this,"path",o);h(this,"o",i);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 Re(e,{media:i.media,fit:s.fit,path:o,onItem:i.onItem,onPassEnd:i.onPassEnd,onApp:i.onApp}),s.transition==="slide"&&this.unplayed.push("transition slide");let d=s.transition==="fade"?{type:"crossfade"}:void 0,p=n.map(c=>r.slide(c,{imageDurationMs:s.imageDurationMs,transition:d,region:!0})).filter(c=>!!c);this.slides=p.length===1&&p[0].untilEnd&&(s.onEnd??"loop")==="loop"?[{...p[0],untilEnd:!1,loop:!0}]:p,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 Rn(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(r=>Kt(r,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(r=>r?.from??0)).filter(s=>s>e);clearTimeout(this.timer),n.length&&(this.timer=setTimeout(()=>{!this.stopped&&!this.ended&&this.first()},Nn(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,...at(s)}),this.o.onItem?.({type:"item.end",element:this.path,item:e.item,blob:e.blob.hash,endedAt:Date.now(),reason:"error"}),this.schedule(_r);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 le(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(r=>r?.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")},Nn(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 ft=t=>t!=null&&t!==""&&!(Array.isArray(t)&&t.length===0);function Dn(t,e,n,s=[]){let r=t[n];if(!r)return!1;if(!r.when)return!0;if(s.includes(n))return!1;let o=i=>String(i);for(let[i,a]of Object.entries(r.when)){if(!t[i]||i===n)return!1;let p=Dn(t,e,i,[...s,n])?e[i]!==void 0?e[i]:t[i].default:void 0,c=Array.isArray(p)?p.map(o):ft(p)?[o(p)]:[];if(a.set!==void 0&&a.set!==ft(p)||a.anyOf&&!(ft(p)&&!Array.isArray(p)?a.anyOf.map(o).includes(o(p)):c.some(l=>a.anyOf.map(o).includes(l)))||a.allOf&&!a.allOf.map(o).every(l=>c.includes(l))||a.noneOf&&c.some(l=>a.noneOf.map(o).includes(l)))return!1}return!0}function jn(t,e){return Object.keys(t).filter(n=>Dn(t,e,n))}var za=de.apiVersion,Fn=new Map(de.commands.map(t=>[t.name,t]));var J=class extends Error{constructor(n,s){super(s);h(this,"code",n)}};function Hn(t,e){let n=e.capability??"read",s={kind:"app",app:e.id,instance:e.instance},r=new Set,o=!1,i=async(a,d={})=>{if(o)throw new J("NOT_ALLOWED","This app has been removed from the screen.");let p=Fn.get(a);if(!p)throw new J("UNKNOWN",`There is no command ${a}.`);if(p.apps==="none")throw new J("NOT_ALLOWED",`Apps cannot call ${a}.`);if(p.apps==="control"&&n!=="control")throw new J("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 J(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 p=t.on(a,d),c=()=>{p(),r.delete(c)};return r.add(c),c},time:()=>i("time.now"),call:i,play:()=>i("player.play"),pause:()=>i("player.pause"),stop:()=>i("player.stop"),next:()=>i("player.next"),previous:()=>i("player.previous"),finish:()=>i("app.finish"),dispose(){o=!0;for(let a of[...r])a()}}}var gt={"Intl.DateTimeFormat":"date formatters (Intl.DateTimeFormat)","Intl.NumberFormat":"number formatters (Intl.NumberFormat)","Intl.Collator":"collators (Intl.Collator)","Intl.PluralRules":"plural rules (Intl.PluralRules)","Intl.RelativeTimeFormat":"relative time formatters (Intl.RelativeTimeFormat)","Intl.ListFormat":"list formatters (Intl.ListFormat)","Intl.DisplayNames":"display names (Intl.DisplayNames)","Intl.Segmenter":"segmenters (Intl.Segmenter)",toLocale:"formatters, through toLocaleString, toLocaleDateString or toLocaleTimeString with options",canvas:"canvases",context:"canvas contexts",bitmap:"image bitmaps (createImageBitmap)",objectUrl:"object URLs (URL.createObjectURL)",worker:"workers",listener:"listeners on window or document"};function Bn(t){let e=0,n=s=>{for(let r=s.firstChild;r;r=r.nextSibling){if(r.nodeType===1){e++;let o=r.shadowRoot;o&&n(o)}n(r)}};return n(t),e}function Un(t,e){let n={},s=c=>{n[c]=(n[c]??0)+1},r=(c,l)=>new Proxy(c,{construct:(m,g,w)=>(s(l),Reflect.construct(m,g,w)),apply:(m,g,w)=>(s(l),Reflect.apply(m,g,w))}),o=t.Intl;for(let c of["DateTimeFormat","NumberFormat","Collator","PluralRules","RelativeTimeFormat","ListFormat","DisplayNames","Segmenter"]){let l=o[c];l&&(o[c]=r(l,`Intl.${c}`))}let i=(c,l,m)=>{let g=c&&c[l];typeof g=="function"&&(c[l]=function(...w){let f=Reflect.apply(g,this,w),b=m(w,this,f);return b&&s(b),f})},a=c=>c[1]!==void 0?"toLocale":null;for(let c of["toLocaleString","toLocaleDateString","toLocaleTimeString"])i(t.Date?.prototype,c,a);i(t.Number?.prototype,"toLocaleString",a),i(t.Document?.prototype,"createElement",c=>String(c[0]).toLowerCase()==="canvas"?"canvas":null),t.OffscreenCanvas&&(t.OffscreenCanvas=r(t.OffscreenCanvas,"canvas"));let d=new WeakSet,p=(c,l,m)=>!m||typeof m!="object"||d.has(m)?null:(d.add(m),"context");return i(t.HTMLCanvasElement?.prototype,"getContext",p),i(t.OffscreenCanvas?.prototype,"getContext",p),i(t,"createImageBitmap",()=>"bitmap"),i(t.URL,"createObjectURL",()=>"objectUrl"),t.Worker&&(t.Worker=r(t.Worker,"worker")),i(t.EventTarget?.prototype,"addEventListener",(c,l)=>l===void 0||l===t||l===t.document?"listener":null),()=>({kinds:{...n},nodes:e()})}var _n=["none","fade","scale","slide-up","slide-left"],De=[["1920 \xD7 1080",1920,1080],["1080 \xD7 1920",1080,1920],["3840 \xD7 2160",3840,2160]],qr=[.25,.5,1,2,4],fe=3e4,Kn=5e3,Wr={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:[]},L={frames:"",tables:{},controller:{status:{},events:{}}},z={people:new Set,player:new Map},Be=[],ge=new Map,wt=new Map,vt=new Set,je=0,E=null,kt=null,xt=null,Zn=-1,W=null,me=0,ee={wall:0,t:0},Fe=0,He=[],j=t=>document.getElementById(t),Mt=j("stage"),zr=j("controls"),qn=j("report"),yt=j("made"),Vr=Un(window,()=>Bn(Mt)),At={kinds:{},nodes:0};addEventListener("message",t=>{let e=t.data;L.frames&&t.origin==="null"&&e?.wyndlassPreview==="made"&&e.made&&typeof e.made=="object"&&(At=e.made)});var Jn=500,Wn=3,D={last:null,reads:[],said:""};function y(t,e={},...n){let s=document.createElement(t);for(let[r,o]of Object.entries(e))o==null||o===!1||(r.startsWith("on")&&typeof o=="function"?s.addEventListener(r.slice(2),o):r==="value"||r in s&&typeof o!="string"?s[r]=o:s.setAttribute(r,o===!0?"":String(o)));for(let r of n)r!=null&&s.append(r);return s}var Kr=0;function S(t,e,n=!1){return e.id||(e.id=`c${++Kr}`),n?y("div",{class:"field inline"},e,y("label",{for:e.id},t)):y("div",{class:"field"},y("label",{for:e.id},t),e)}function q(t,e,n){let s=y("select",{onchange:()=>n(s.value)},...t.map(r=>{let[o,i]=Array.isArray(r)?r:[r,r];return y("option",{value:o,selected:o===e},i)}));return s}async function zn(){let[t,e,n,s]=await Promise.all([fetch("/app.json").then(i=>i.json()),fetch("/app.js"),fetch("/samples.json").then(i=>i.json()),fetch("/config.json").then(i=>i.json())]);L=s;for(let[i,a]of Object.entries(L.tables))u.tables.has(Ue(i))||St(i,a);E=t,Zn=Number(e.headers.get("x-preview-version")??-1),xt=e.ok?null:await e.text(),kt=e.ok?new Uint8Array(await e.arrayBuffer()):null;let r=E.params??{},o={};for(let[i,a]of Object.entries(r))o[i]=u.touched.has(i)?u.props[i]:a.default;u.props=o;for(let[i,a]of Object.entries(n))i in u.samples||(u.samples[i]=a);Et(),document.title=`${E.name}: Wyndlass app preview`,j("title").textContent=E.name,j("subtitle").textContent=`${E.id} ${E.version}`}var Ue=t=>`tbl_${(t.replace(/\.(json|csv)$/i,"").toLowerCase().replace(/[^a-z0-9]+/g,"")||"table").slice(0,26).padEnd(26,"0")}`;function Zr(t){let e=[],n=[],s="",r=!1;for(let o=0;o<t.length;o++){let i=t[o];if(r){i==='"'&&t[o+1]==='"'?(s+='"',o++):i==='"'?r=!1:s+=i;continue}i==='"'?r=!0:i===","?(n.push(s),s=""):i===`
4
+ ${n}`))).slice(0,8),16),o=Wr(r),i=Array.from({length:s},(a,l)=>l);for(let a=s-1;a>0;a--){let l=Math.floor(o()*(a+1));[i[a],i[l]]=[i[l],i[a]]}return i}var zr=250,Vr=1e3;function Gt(t,e){let n=t.animation?.duration;if(n?.kind==="fixed")return{durationMs:n.ms};if(n?.kind==="untilPlaylistEnds"){let s=jn(t.elements,n.element);return{durationMs:(s?.source?.kind==="playlist"?e.playlist(s.source.item)??[]:s?.source?[{item:s.source.item}]:[]).reduce((i,a)=>i+(e.slide(a,{imageDurationMs:s?.playback?.imageDurationMs})?.durationMs??0),0)||1e4,untilPass:n.element}}return{durationMs:ue(t.animation?.clips??[]).autoDurationMs}}function Dn(t,e,n=0){let s={},r=o=>{for(let i of o){if(i.type==="library"&&i.source){let a=i.source.kind==="playlist"?e.playlist(i.source.item)??[]:[{item:i.source.item}];s[i.source.item.id]={video:a.some(l=>{let p=e.slide(l);if(!p)return!1;if(p.blob.contentType.startsWith("video/"))return!0;let c=p.layout;return!!c&&n<4&&Object.values(Dn(c.doc,c.source,n+1)).some(d=>d.video)})}}i.children&&r(i.children)}};return r(t.elements),s}function jn(t,e){for(let n of t){if(n.id===e)return n;let s=n.children&&jn(n.children,e);if(s)return s}}var Q=class{constructor(e,n,s,r={}){h(this,"source",n);h(this,"o",r);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,"bound",[]);h(this,"unheard");h(this,"reduced",typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches);let o=s.clientWidth||window.innerWidth,i=s.clientHeight||window.innerHeight,a=!e.profiles[0].width,l=a?{...e.profiles[0],width:o,height:i}:e.profiles[0];a&&(e={...e,elements:e.elements.map(v=>v.frame.w?v:{...v,frame:{x:0,y:0,w:o,h:i}})});let p=Math.min(o/l.width,i/l.height);this.el=H("div"),this.el.dataset.layout=l.id,this.el.style.cssText=`position:absolute;left:${(o-l.width*p)/2}px;top:${(i-l.height*p)/2}px;width:${l.width}px;height:${l.height}px;transform:scale(${p});transform-origin:0 0;overflow:hidden`,s.append(this.el),this.doc=e;let c=new Map,d=v=>{for(let A of v)c.set(A.id,A.frame),A.children&&d(A.children)};d(e.elements);let m=In(e.animation?.clips??[],c);this.motions=m.motions;let y=new Set(m.unplayed.map(v=>`preset ${v}`)),w=[this.loadFonts()],f=r.media?An(e,void 0,Dn(e,n),r.media.capability):[];this.native=new Set(f.filter(v=>v.path.path==="native").map(v=>v.element));let b=(v,A)=>{for(let k of v){if(k.hidden)continue;let I=this.element(k,y,w);A.append(I),k.children?.length&&k.type==="shape"&&b(k.children,I)}};b(e.elements,this.el),e.background&&wn(this.el,e.background,l,f.filter(v=>this.native.has(v.element)).map(v=>v.box)),this.unplayed=[...y].sort(),this.ready=Promise.all(w).then(()=>{}),this.bound.length&&(this.unheard=n.onSnapshots?.(()=>this.refill()))}refill(){let e=ce(this.source),n=(this.heldAt??Date.now())-this.startedAt;for(let{el:s,e:r}of this.bound){let o=ht(r,e),i=this.animated.find(a=>a.el===s);i?(i.text=o,this.running||this.place(i,n)):s.textContent!==o&&(s.textContent=o)}}path(e){return this.o.path?`${this.o.path}/${e}`:e}element(e,n,s){let r=vn(e);kn(r,e,this.source),e.type==="text"&&(e.bind||Ce(e.text??""))&&this.bound.push({el:r,e});let o=this.motions.get(e.id);if(o){let i={el:r,e,motion:o,text:e.type==="text"?r.textContent??"":void 0};this.animated.push(i),this.place(i,0)}if(e.type==="library"&&e.source){let i=e.source.kind==="playlist"?this.source.playlist(e.source.item)??[]:[{item:e.source.item}],a=new ft(r,i,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 l of a.unplayed)n.add(l)}else if(e.type==="app"){let i=e.app?this.source.app?.(e.app):null;i?s.push(this.mountApp(e,r,i)):n.add(`app ${e.app?.id??e.id}`)}return r}place(e,n){Nn(e.el,e.e,Cn(e.motion,e.e.frame,n,this.reduced),e.text);let s=e.e.type==="text"?e.el.firstElementChild:null;s&&Ge(s)}loadFonts(){return Mn(this.doc.fonts??[],e=>this.source.font(e))}async mountApp(e,n,s){let r=this.path(e.id),o=e.app,i=(e.appInfo??s.info)?.id,a={now:()=>this.heldAt??Date.now(),startedAt:()=>this.startedAt||Date.now()},l=(d,m)=>this.o.onApp?.({type:"app.failed",element:r,item:o,...i?{app:i}:{},reason:d,...m?{error:m}:{}}),p=this.o.isolate?{...this.o.isolate,fonts:this.doc.fonts,font:d=>this.source.font(d)}:void 0,c=await xn(e,n,s,this.source,a,this.o.appDeadlineMs??5e3,d=>{this.disposed?d.dispose():this.apps.push({app:d,box:n,path:r,id:e.id,item:o})},d=>l("error",d),p);if(!("held"in c)){if("failed"in c){l(c.failed,c.error);return}c.late&&(this.late.push(r),this.o.onAppLate?.(r,o.id),l("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(),r=s-this.startedAt;for(let o of this.animated)this.place(o,r);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=ue(n).clips,r=typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches;for(let o of this.apps){let a=n.map((f,b)=>({c:f,t:s[b]})).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 l=o.box,p=this.animated.findIndex(f=>f.el===l);p>=0&&(this.animated.splice(p,1),Object.assign(l.style,{opacity:"",transform:"",clipPath:""}));let c=(f,b,v)=>this.hook(o,f.kind,{startAt:b,preset:f.preset??"fade",durationMs:f.durationMs,easing:f.easing,delayMs:0,...f.staggerMs?{staggerMs:f.staggerMs}:{},...v!==void 0?{page:v}:{},reducedMotion:r},l);for(let{c:f,t:b}of a)c(f,e+b.startMs);let d=o.app.paging();if(!d)continue;let m=a.find(f=>f.c.kind==="enter")?.c,y=a.find(f=>f.c.kind==="exit")?.c,w=Rn(d.count,d.ms,Gt(this.doc,this.source).durationMs);for(let f=1;f<w.length;f++){let b=e+w[f].startMs;y&&c(y,b-y.durationMs,w[f-1].page),m&&c(m,b,w[f].page)}}}hook(e,n,s,r){let o=!1;e.app.hook(n,s).finally(()=>{o=!0});let i=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"&&(r.style.visibility="hidden"))},Math.max(0,i+zr-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.unheard?.(),this.el.remove()}},$n=t=>Math.min(Math.max(0,t),2**31-1),ft=class{constructor(e,n,s,r,o,i,a){h(this,"region",e);h(this,"playback",s);h(this,"source",r);h(this,"path",o);h(this,"o",i);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 Ne(e,{media:i.media,fit:s.fit,path:o,onItem:i.onItem,onPassEnd:i.onPassEnd,onApp:i.onApp}),s.transition==="slide"&&this.unplayed.push("transition slide");let l=s.transition==="fade"?{type:"crossfade"}:void 0,p=n.map(c=>r.slide(c,{imageDurationMs:s.imageDurationMs,transition:l,region:!0})).filter(c=>!!c);this.slides=p.length===1&&p[0].untilEnd&&(s.onEnd??"loop")==="loop"?[{...p[0],untilEnd:!1,loop:!0}]:p,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 On(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(r=>Jt(r,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(r=>r?.from??0)).filter(s=>s>e);clearTimeout(this.timer),n.length&&(this.timer=setTimeout(()=>{!this.stopped&&!this.ended&&this.first()},$n(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,...lt(s)}),this.o.onItem?.({type:"item.end",element:this.path,item:e.item,blob:e.blob.hash,endedAt:Date.now(),reason:"error"}),this.schedule(Vr);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 le(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(r=>r?.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")},$n(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 gt=t=>t!=null&&t!==""&&!(Array.isArray(t)&&t.length===0);function Fn(t,e,n,s=[]){let r=t[n];if(!r)return!1;if(!r.when)return!0;if(s.includes(n))return!1;let o=i=>String(i);for(let[i,a]of Object.entries(r.when)){if(!t[i]||i===n)return!1;let p=Fn(t,e,i,[...s,n])?e[i]!==void 0?e[i]:t[i].default:void 0,c=Array.isArray(p)?p.map(o):gt(p)?[o(p)]:[];if(a.set!==void 0&&a.set!==gt(p)||a.anyOf&&!(gt(p)&&!Array.isArray(p)?a.anyOf.map(o).includes(o(p)):c.some(d=>a.anyOf.map(o).includes(d)))||a.allOf&&!a.allOf.map(o).every(d=>c.includes(d))||a.noneOf&&c.some(d=>a.noneOf.map(o).includes(d)))return!1}return!0}function Hn(t,e){return Object.keys(t).filter(n=>Fn(t,e,n))}var el=de.apiVersion,Bn=new Map(de.commands.map(t=>[t.name,t]));var J=class extends Error{constructor(n,s){super(s);h(this,"code",n)}};function Un(t,e){let n=e.capability??"read",s={kind:"app",app:e.id,instance:e.instance},r=new Set,o=!1,i=async(a,l={})=>{if(o)throw new J("NOT_ALLOWED","This app has been removed from the screen.");let p=Bn.get(a);if(!p)throw new J("UNKNOWN",`There is no command ${a}.`);if(p.apps==="none")throw new J("NOT_ALLOWED",`Apps cannot call ${a}.`);if(p.apps==="control"&&n!=="control")throw new J("NOT_ALLOWED",`${a} needs capabilities.player: control in ${e.id}'s definition.`);let c=await t.call(a,l,s);if(c.ok)return c.result;if(c.code==="PREVIEW")return{applied:!1,reason:"preview"};throw new J(c.code,c.message)};return{app:e.id,instance:e.instance,capability:n,status:()=>t.status(),get:a=>t.status()[a],on(a,l){if(o)return()=>{};let p=t.on(a,l),c=()=>{p(),r.delete(c)};return r.add(c),c},time:()=>i("time.now"),call:i,play:()=>i("player.play"),pause:()=>i("player.pause"),stop:()=>i("player.stop"),next:()=>i("player.next"),previous:()=>i("player.previous"),finish:()=>i("app.finish"),dispose(){o=!0;for(let a of[...r])a()}}}var yt={"Intl.DateTimeFormat":"date formatters (Intl.DateTimeFormat)","Intl.NumberFormat":"number formatters (Intl.NumberFormat)","Intl.Collator":"collators (Intl.Collator)","Intl.PluralRules":"plural rules (Intl.PluralRules)","Intl.RelativeTimeFormat":"relative time formatters (Intl.RelativeTimeFormat)","Intl.ListFormat":"list formatters (Intl.ListFormat)","Intl.DisplayNames":"display names (Intl.DisplayNames)","Intl.Segmenter":"segmenters (Intl.Segmenter)",toLocale:"formatters, through toLocaleString, toLocaleDateString or toLocaleTimeString with options",canvas:"canvases",context:"canvas contexts",bitmap:"image bitmaps (createImageBitmap)",objectUrl:"object URLs (URL.createObjectURL)",worker:"workers",listener:"listeners on window or document"};function _n(t){let e=0,n=s=>{for(let r=s.firstChild;r;r=r.nextSibling){if(r.nodeType===1){e++;let o=r.shadowRoot;o&&n(o)}n(r)}};return n(t),e}function qn(t,e){let n={},s=c=>{n[c]=(n[c]??0)+1},r=(c,d)=>new Proxy(c,{construct:(m,y,w)=>(s(d),Reflect.construct(m,y,w)),apply:(m,y,w)=>(s(d),Reflect.apply(m,y,w))}),o=t.Intl;for(let c of["DateTimeFormat","NumberFormat","Collator","PluralRules","RelativeTimeFormat","ListFormat","DisplayNames","Segmenter"]){let d=o[c];d&&(o[c]=r(d,`Intl.${c}`))}let i=(c,d,m)=>{let y=c&&c[d];typeof y=="function"&&(c[d]=function(...w){let f=Reflect.apply(y,this,w),b=m(w,this,f);return b&&s(b),f})},a=c=>c[1]!==void 0?"toLocale":null;for(let c of["toLocaleString","toLocaleDateString","toLocaleTimeString"])i(t.Date?.prototype,c,a);i(t.Number?.prototype,"toLocaleString",a),i(t.Document?.prototype,"createElement",c=>String(c[0]).toLowerCase()==="canvas"?"canvas":null),t.OffscreenCanvas&&(t.OffscreenCanvas=r(t.OffscreenCanvas,"canvas"));let l=new WeakSet,p=(c,d,m)=>!m||typeof m!="object"||l.has(m)?null:(l.add(m),"context");return i(t.HTMLCanvasElement?.prototype,"getContext",p),i(t.OffscreenCanvas?.prototype,"getContext",p),i(t,"createImageBitmap",()=>"bitmap"),i(t.URL,"createObjectURL",()=>"objectUrl"),t.Worker&&(t.Worker=r(t.Worker,"worker")),i(t.EventTarget?.prototype,"addEventListener",(c,d)=>d===void 0||d===t||d===t.document?"listener":null),()=>({kinds:{...n},nodes:e()})}var Wn=["none","fade","scale","slide-up","slide-left"],je=[["1920 \xD7 1080",1920,1080],["1080 \xD7 1920",1080,1920],["3840 \xD7 2160",3840,2160]],Kr=[.25,.5,1,2,4],fe=3e4,Jn=5e3,Zr={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:[]},E={frames:"",tables:{},controller:{status:{},events:{}}},vt,Jr={"connection.changed":{type:"connection.changed",seq:0,at:"2026-10-01T14:02:00Z",connection:"offline"},"audio.changed":{type:"audio.changed",seq:0,at:"2026-10-01T14:03:00Z",audio:{volume:.3,muted:!0},by:{kind:"player"}}},z={people:new Set,player:new Map},be=[],ge=new Map,kt=new Map,xt=new Set,Fe=0,T=null,Mt=null,At=null,Yn=-1,W=null,me=0,ee={wall:0,t:0},He=0,Be=[],O=t=>document.getElementById(t),St=O("stage"),Yr=O("controls"),zn=O("report"),bt=O("made"),Xr=qn(window,()=>_n(St)),Et={kinds:{},nodes:0};addEventListener("message",t=>{let e=t.data;E.frames&&t.origin==="null"&&e?.wyndlassPreview==="made"&&e.made&&typeof e.made=="object"&&(Et=e.made)});var Xn=500,Vn=3,j={last:null,reads:[],said:""};function g(t,e={},...n){let s=document.createElement(t);for(let[r,o]of Object.entries(e))o==null||o===!1||(r.startsWith("on")&&typeof o=="function"?s.addEventListener(r.slice(2),o):r==="value"||r in s&&typeof o!="string"?s[r]=o:s.setAttribute(r,o===!0?"":String(o)));for(let r of n)r!=null&&s.append(r);return s}var Gr=0;function S(t,e,n=!1){return e.id||(e.id=`c${++Gr}`),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 q(t,e,n){let s=g("select",{onchange:()=>n(s.value)},...t.map(r=>{let[o,i]=Array.isArray(r)?r:[r,r];return g("option",{value:o,selected:o===e},i)}));return s}async function Kn(){let[t,e,n,s]=await Promise.all([fetch("/app.json").then(i=>i.json()),fetch("/app.js"),fetch("/samples.json").then(i=>i.json()),fetch("/config.json").then(i=>i.json())]);E=s,E.controller.status=vt??(vt=E.controller.status),E.controller.events={...E.controller.events,...Jr};for(let[i,a]of Object.entries(E.tables))u.tables.has(Ue(i))||Tt(i,a);T=t,Yn=Number(e.headers.get("x-preview-version")??-1),At=e.ok?null:await e.text(),Mt=e.ok?new Uint8Array(await e.arrayBuffer()):null;let r=T.params??{},o={};for(let[i,a]of Object.entries(r))o[i]=u.touched.has(i)?u.props[i]:a.default;u.props=o;for(let[i,a]of Object.entries(n))i in u.samples||(u.samples[i]=a);Pt(),document.title=`${T.name}: Wyndlass app preview`,O("title").textContent=T.name,O("subtitle").textContent=`${T.id} ${T.version}`}var Ue=t=>`tbl_${(t.replace(/\.(json|csv)$/i,"").toLowerCase().replace(/[^a-z0-9]+/g,"")||"table").slice(0,26).padEnd(26,"0")}`;function Qr(t){let e=[],n=[],s="",r=!1;for(let o=0;o<t.length;o++){let i=t[o];if(r){i==='"'&&t[o+1]==='"'?(s+='"',o++):i==='"'?r=!1:s+=i;continue}i==='"'?r=!0:i===","?(n.push(s),s=""):i===`
5
5
  `||i==="\r"?(i==="\r"&&t[o+1]===`
6
- `&&o++,n.push(s),e.push(n),n=[],s=""):s+=i}return(s||n.length)&&(n.push(s),e.push(n)),e.filter(o=>o.some(i=>i!==""))}function Jr(t,e){let n="";for(let r of t.normalize("NFD").replace(/\p{Mn}/gu,""))/[A-Za-z0-9]/.test(r)?n+=r.toLowerCase():n&&!n.endsWith("_")&&(n+="_");n=n.replace(/^_+|_+$/g,""),/^[a-z]/.test(n)||(n=`c_${n}`),n=n.length>60?n.slice(0,60).replace(/_+$/,""):n;let s=n;for(let r=2;[...e].some(o=>o.toLowerCase()===s);r++)s=`${n}_${r}`;return e.add(s),s}function Yn(t,e){let n=Ue(t),s=(o,i)=>({table:n,version:1,rows:i,columns:o.map(({key:a,label:d})=>({key:a,label:d,type:i.length&&i.every(p=>p[a]===""||p[a]===void 0||Number.isFinite(Number(p[a])))?"number":"text"}))});if(/\.csv$/i.test(t)){let[o=[],...i]=Zr(e),a=new Set,d=o.map(l=>({label:l.trim(),key:Jr(l.trim()||"column",a)})),p=i.map(l=>Object.fromEntries(d.map((m,g)=>[m.key,l[g]??""]))),c=s(d,p);for(let l of c.columns)if(l.type==="number")for(let m of p)m[l.key]!==""&&(m[l.key]=Number(m[l.key]));return c}let r=JSON.parse(e);if(r&&typeof r=="object"&&"columns"in r&&"rows"in r)return{...r,table:n};if(Array.isArray(r))return s([...new Set(r.flatMap(o=>Object.keys(o)))].map(o=>({key:o,label:o})),r);throw new Error(`${t} is not a table: a snapshot, an array of rows, or CSV`)}function St(t,e){wt.set(t,e);try{u.tables.set(Ue(t),Ce(Yn(t,e))),ge.delete(t)}catch(n){ge.set(t,String(n.message))}}function Xn(t,e,n){let s=u.tables.get(t);if(!s)return;u.tables.set(t,Ce({...s,version:s.version+1,rows:e,...n?{columns:n}:{}}));for(let i of[...vt])i([t]);let r=document.querySelector(`[data-testid="table"][data-table="${t}"]`);r&&(r.textContent=es(u.tables.get(t)));let o=document.querySelector(`textarea[data-rows="${t}"]`);o&&document.activeElement!==o&&(o.value=JSON.stringify(e,null,2))}async function Yr(){let t=await fetch("/config.json").then(n=>n.json()),e=!1;for(let[n,s]of Object.entries(t.tables)){if(wt.get(n)===s)continue;let r=Ue(n);if(!u.tables.has(r)){St(n,s),e=!0;continue}wt.set(n,s);try{let o=Yn(n,s);ge.delete(n),Xn(r,o.rows,o.columns)}catch(o){ge.set(n,String(o.message)),te()}}e&&(Et(),te(),await ye())}function Et(){let t=[...u.tables.keys()][0];if(!(!t||!E))for(let[e,n]of Object.entries(E.params??{}))n.type==="datatable"&&!u.props[e]&&(u.props[e]=t,u.touched.add(e))}function Xr(){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 z.people.add(t),()=>{z.people.delete(t)}}}}function bt(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 z.people)s([n]);j("people-count").textContent=`${u.people.length} in view`}var Gr={status:()=>L.controller.status,on(t,e){let n=z.player.get(t)??new Set;return n.add(e),z.player.set(t,n),()=>{n.delete(e)}},async call(t,e,n){return Be.push(`controller: ${n.app??"the app"} called ${t}, not applied (a preview)`),Q({}),{ok:!1,code:"PREVIEW",message:"a preview"}}},Qr={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=>r=>{throw Be.push(`controller: ${s} refused, ${r.code??"error"}: ${r.message}`),Q({}),r},n={...t,call:(s,r)=>t.call(s,r).catch(e(s))};for(let[s,r]of Object.entries(Qr))n[s]=()=>t[s]().catch(e(r));return n}function ti(t){for(let e of[t.type,"*"])for(let n of z.player.get(e)??[])n(t)}function ni(){return new Oe({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 si(){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:fe-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:Wr,props:u.props,...E.slots&&u.tables.size?{binding:{table:[...u.tables.keys()][0]}}:{},...E.capabilities?.sensors?{sensors:E.capabilities.sensors}:{},appInfo:{id:E.id,element:E.element,player:E.capabilities?.player==="control"?"control":"read"}}],animation:{duration:{kind:"fixed",ms:fe},clips:t}}}async function ye(){let t=++me;if(cancelAnimationFrame(Fe),W?.dispose(),W=null,Mt.replaceChildren(),He.length=0,Be.length=0,D.last=null,D.reads=[],At={kinds:{},nodes:0},!E||!kt){Q({building:!0});return}let e={};try{e=JSON.parse(u.screen.fields||"{}")}catch{}let n=kt,s={deviceId:"preview",slide:()=>null,playlist:()=>null,font:()=>{},app:()=>({hash:`preview-${me}`,bytes:n}),data:ni(),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,onSnapshots:a=>(vt.add(a),()=>{vt.delete(a)}),...L.frames?{}:{...E.capabilities?.sensors?.includes("impressions")?{impressions:Xr()}:{},controllerFor:a=>ei(Hn(Gr,a))}};z.people.clear(),z.player.clear();let r=performance.now(),o=!1;je++;let i=new G(si(),s,Mt,{appDeadlineMs:Kn,...L.frames?{isolate:{frames:L.frames,apps:()=>!0}}:{},onAppLate:()=>{o=!0},onApp:a=>{t===me&&(He.push(`app.failed: ${a.reason}${a.error?` (${a.error})`:""}`),Q({}))}});W=i,Q({waiting:!0}),await i.ready,t===me&&(Q({readyMs:o?void 0:Math.round(performance.now()-r),late:o}),await i.start(Date.now()-u.t),t===me&&(i.seek(u.t),ee={wall:Date.now(),t:u.t},ri(),ii()))}function ri(){cancelAnimationFrame(Fe);let t=()=>{if(W){if(u.playing){if(u.t=ee.t+(Date.now()-ee.wall)*u.speed,u.t>=fe){u.t=0,ye();return}W.seek(u.t)}j("clock-seek").value=String(Math.round(u.t)),j("clock-time").textContent=`${(u.t/1e3).toFixed(1)} s`,Fe=requestAnimationFrame(t)}};Fe=requestAnimationFrame(t)}function ii(){let t=j("pages"),e=W?.paging("app")??null;t.replaceChildren(e&&e.count>1?y("div",{},y("p",{"data-testid":"pages"},`${e.count} pages of ${(e.ms/1e3).toFixed(1)} s`),y("div",{class:"row"},...Array.from({length:e.count},(n,s)=>y("button",{onclick:()=>Gn(s*e.ms)},`Page ${s+1}`)))):y("p",{"data-testid":"pages"},"One page"))}function Gn(t){u.t=Math.max(0,Math.min(fe-1,t)),ee={wall:Date.now(),t:u.t},W?.seek(u.t)}function Q(t){let e=[];if(xt?e.push(y("pre",{"data-testid":"build-error"},`The module does not build:
7
- ${xt}`)):t.building&&e.push(y("span",{},"Building\u2026")),t.waiting&&e.push(y("span",{"data-testid":"ready"},"Waiting for ready()\u2026")),t.late)e.push(y("span",{class:"late","data-testid":"ready"},`Late: not ready within ${Kn/1e3} s. A screen reports app.failed (late) and shows the app as it is.`));else if(t.readyMs!==void 0)e.push(y("span",{class:"ok","data-testid":"ready"},`Ready in ${t.readyMs} ms`));else if(!t.waiting&&!t.building){let n=qn.querySelector('[data-testid="ready"]');n&&e.push(n)}for(let n of He)e.push(y("span",{class:"failed","data-testid":"failure"},n));L.frames&&He.some(n=>/SecurityError/.test(n))&&e.push(y("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 Be.slice(-5))e.push(y("span",{"data-testid":"command"},n));je&&e.push(y("span",{"data-testid":"made-times"},`Made ${je===1?"once":`${je} times`}: at each start of its layout, and when its code, params or screen change. A table's change reaches it as it plays.`)),qn.replaceChildren(...e)}function oi(){let t=L.frames?At:Vr();if(D.last&&u.playing&&W){let i={};for(let[a,d]of Object.entries(t.kinds)){let p=d-(D.last.kinds[a]??0);p>0&&(i[a]=p)}D.reads=[...D.reads,i].slice(-Wn)}D.last=t;let e=D.reads.length===Wn?Object.keys(gt).filter(i=>D.reads.every(a=>a[i])):[],n=i=>Math.round(D.reads.reduce((a,d)=>a+(d[i]??0),0)*1e3/(Jn*D.reads.length)),s=e.map(i=>[i,`Makes ${n(i)} ${gt[i]} a second while it plays. ${i==="listener"?"Add a listener once, in mount(), and take it off when the app goes":"Make it once, in mount() or with useMemo, never in draw() or a render"}: one made at every frame holds memory a screen frees late, and can run it out.`]),r=`${e.length?"":"Nothing made at every frame. "}${t.nodes} DOM nodes in the app.`,o=JSON.stringify(s);if(o===D.said&&yt.firstChild){yt.querySelector('[data-testid="made-counts"]').textContent=r;return}D.said=o,yt.replaceChildren(...s.map(([i,a])=>y("span",{class:"warn","data-testid":"made","data-kind":i},a)),y("span",{"data-testid":"made-counts"},r))}function ai(){let t=Object.entries(E?.params??{}).find(([,n])=>n.type==="datatable")?.[0],e=E?.slots?[...u.tables.keys()][0]:t?u.props[t]:void 0;return typeof e=="string"?u.tables.get(e):void 0}var Qn=t=>t.label&&t.label!==t.key?`${t.label} (${t.key})`:t.key,es=t=>`${t.table}: ${t.rows.length} rows, ${t.columns.map(Qn).join(", ")}`;function li(t,e){let n=u.props[t],s=o=>{u.props[t]=o,u.touched.add(t),ts(),R()},r=e.label??t;if(e.type==="boolean")return S(r,y("input",{type:"checkbox",checked:n===!0,onchange:o=>s(o.target.checked)}),!0);if(e.type==="enum"){let o=(e.values??[]).map(i=>i&&typeof i=="object"?[String(i.value),String(i.label??i.value)]:String(i));return S(r,q(o,String(n??""),s))}if(e.type==="number")return S(r,y("input",{type:"number",value:n===void 0?"":String(n),min:e.min,max:e.max,step:e.step??"any",onchange:o=>{let i=o.target.value;s(i===""?void 0:Number(i))}}));if(["string","text","color","url","datetime","duration","secret"].includes(e.type)){let o=e.type==="text"?y("textarea",{value:String(n??""),onchange:i=>s(i.target.value)}):y("input",{type:"text",value:String(n??""),placeholder:e.type==="color"?"#rrggbb or #rrggbbaa":void 0,onchange:i=>s(i.target.value)});return S(r,o)}return e.type==="datatable"?S(r,q(["",...u.tables.keys()],String(n??""),s)):e.type==="column"?S(r,q(["",...(ai()?.columns??[]).map(o=>[o.key,Qn(o)])],String(n??""),s)):S(`${r} (JSON)`,y("textarea",{value:n===void 0?"":JSON.stringify(n,null,2),onchange:o=>{let i=o.target.value.trim();try{s(i?JSON.parse(i):void 0)}catch{s(i)}}}))}function ts(){let t=j("params"),e=E?.params??{},n=new Set(jn(e,u.props));t.replaceChildren(...Object.entries(e).filter(([s])=>n.has(s)).map(([s,r])=>li(s,r))),t.children.length||t.append(y("p",{},"No params."))}var Vn;function R(){clearTimeout(Vn),Vn=setTimeout(()=>{ye()},150)}function te(){let t=!De.some(([,i,a])=>i===u.width&&a===u.height),e=y("input",{type:"number",value:String(u.width),min:64,max:7680,onchange:i=>{u.width=Number(i.target.value)||1920,R()}}),n=y("input",{type:"number",value:String(u.height),min:64,max:7680,onchange:i=>{u.height=Number(i.target.value)||1080,R()}}),s=y("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)),ee={wall:Date.now(),t:u.t}}},u.playing?"Pause":"Play"),r=y("input",{id:"clock-seek",type:"range",min:0,max:fe,step:100,value:String(u.t),oninput:i=>Gn(Number(i.target.value))}),o=[y("h2",{},"Screen"),S("Size",q([...De.map(([i])=>i),"Custom"],t?"Custom":De.find(([,i,a])=>i===u.width&&a===u.height)[0],i=>{let a=De.find(([d])=>d===i);a&&(u.width=a[1],u.height=a[2]),te(),R()})),t?y("div",{class:"row"},S("Width",e),S("Height",n)):null,y("div",{class:"row"},S("Time zone",y("input",{type:"text",value:u.timeZone,onchange:i=>{u.timeZone=i.target.value||"UTC",R()}})),S("Locale",y("input",{type:"text",value:u.locale,onchange:i=>{u.locale=i.target.value||"en-US",R()}}))),S("Screen's name",y("input",{type:"text",value:u.screen.name,onchange:i=>{u.screen.name=i.target.value,R()}})),y("div",{class:"row"},S("Latitude",y("input",{type:"number",step:"any",value:String(u.screen.lat),onchange:i=>{u.screen.lat=Number(i.target.value),R()}})),S("Longitude",y("input",{type:"number",step:"any",value:String(u.screen.lon),onchange:i=>{u.screen.lon=Number(i.target.value),R()}}))),S("Screen's fields (JSON)",y("textarea",{value:u.screen.fields,onchange:i=>{u.screen.fields=i.target.value,R()}})),y("h2",{},"Clock"),y("div",{class:"row"},s,S("Speed",q(qr.map(i=>[String(i),`${i}\xD7`]),String(u.speed),i=>{ee={wall:Date.now(),t:u.t},u.speed=Number(i)})),y("span",{id:"clock-time"},"0.0 s")),S("Time in the layout",r),y("h2",{},"Enter and exit"),y("div",{class:"row"},S("Enter",q(_n,u.enter,i=>{u.enter=i,R()})),S("Exit",q(_n,u.exit,i=>{u.exit=i,R()}))),S("Each lasts (ms)",y("input",{type:"number",min:0,max:5e3,step:100,value:String(u.clipMs),onchange:i=>{u.clipMs=Number(i.target.value)||0,R()}})),y("div",{id:"pages"}),y("h2",{},"Params"),y("div",{id:"params"}),L.frames?y("p",{"data-testid":"framed"},`In a frame on ${L.frames}, as in the CMS: no storage, cookies, parent page, controller or impressions.`):null,y("h2",{},"Tables"),...[...u.tables.values()].flatMap(i=>[y("p",{"data-testid":"table","data-table":i.table},es(i)),S(`${i.table}'s rows (JSON)`,y("textarea",{"data-rows":i.table,value:JSON.stringify(i.rows,null,2),rows:6,onchange:a=>{try{let d=JSON.parse(a.target.value);Array.isArray(d)&&Xn(i.table,d)}catch{}}}))]),...[...ge].map(([i,a])=>y("p",{class:"failed","data-testid":"table-error"},`${i}: ${a}`)),S("Add a table (JSON or CSV)",y("input",{type:"file",accept:".json,.csv",onchange:async i=>{let a=i.target.files?.[0];a&&(St(a.name,await a.text()),Et(),te(),R())}})),...E?.capabilities?.sensors?.includes("impressions")&&!L.frames?[y("h2",{},"People at the screen"),y("div",{class:"row"},y("button",{type:"button",onclick:()=>bt("enter")},"Someone arrives"),y("button",{type:"button",onclick:()=>bt("look")},"Someone looks"),y("button",{type:"button",onclick:()=>bt("leave")},"Someone leaves")),y("p",{id:"people-count"},`${u.people.length} in view`)]:[],...L.frames?[]:[y("h2",{},"The player's controller"),y("p",{},`The app may ${E?.capabilities?.player==="control"?"read it and call its commands":"read it (capabilities.player: read)"}. Commands are not applied here.`),...Object.keys(L.controller.events).length?[S("Event",q(Object.keys(L.controller.events),Object.keys(L.controller.events)[0],i=>{j("event-json").value=JSON.stringify(L.controller.events[i],null,2)})),S("Its JSON",y("textarea",{id:"event-json",rows:6,value:JSON.stringify(Object.values(L.controller.events)[0],null,2)})),y("button",{type:"button",onclick:()=>{try{ti(JSON.parse(j("event-json").value))}catch{}}},"Send to the app")]:[]],y("h2",{},"Data"),...Object.keys(u.samples).map(i=>S(`${i} answer's data (JSON)`,y("textarea",{"data-source":i,value:JSON.stringify(u.samples[i],null,2),rows:8,onchange:a=>{try{u.samples[i]=JSON.parse(a.target.value),R()}catch{}}}))),Object.keys(u.samples).length?null:y("p",{},"The app declares no data source with a sample (weather, news or calendar).")];zr.replaceChildren(...o.filter(i=>i!==null)),ts()}async function di(){await zn(),te(),setInterval(oi,Jn),await ye();let t=new EventSource("/events"),e=()=>{zn().then(()=>(te(),ye()))};t.addEventListener("hello",n=>{JSON.parse(n.data).version!==Zn&&e()}),t.addEventListener("reload",e),t.addEventListener("tables",()=>{Yr()}),window.addEventListener("resize",()=>R())}di();})();
6
+ `&&o++,n.push(s),e.push(n),n=[],s=""):s+=i}return(s||n.length)&&(n.push(s),e.push(n)),e.filter(o=>o.some(i=>i!==""))}function ei(t,e){let n="";for(let r of t.normalize("NFD").replace(/\p{Mn}/gu,""))/[A-Za-z0-9]/.test(r)?n+=r.toLowerCase():n&&!n.endsWith("_")&&(n+="_");n=n.replace(/^_+|_+$/g,""),/^[a-z]/.test(n)||(n=`c_${n}`),n=n.length>60?n.slice(0,60).replace(/_+$/,""):n;let s=n;for(let r=2;[...e].some(o=>o.toLowerCase()===s);r++)s=`${n}_${r}`;return e.add(s),s}function Gn(t,e){let n=Ue(t),s=(o,i)=>({table:n,version:1,rows:i,columns:o.map(({key:a,label:l})=>({key:a,label:l,type:i.length&&i.every(p=>p[a]===""||p[a]===void 0||Number.isFinite(Number(p[a])))?"number":"text"}))});if(/\.csv$/i.test(t)){let[o=[],...i]=Qr(e),a=new Set,l=o.map(d=>({label:d.trim(),key:ei(d.trim()||"column",a)})),p=i.map(d=>Object.fromEntries(l.map((m,y)=>[m.key,d[y]??""]))),c=s(l,p);for(let d of c.columns)if(d.type==="number")for(let m of p)m[d.key]!==""&&(m[d.key]=Number(m[d.key]));return c}let r=JSON.parse(e);if(r&&typeof r=="object"&&"columns"in r&&"rows"in r)return{...r,table:n};if(Array.isArray(r))return s([...new Set(r.flatMap(o=>Object.keys(o)))].map(o=>({key:o,label:o})),r);throw new Error(`${t} is not a table: a snapshot, an array of rows, or CSV`)}function Tt(t,e){kt.set(t,e);try{u.tables.set(Ue(t),Re(Gn(t,e))),ge.delete(t)}catch(n){ge.set(t,String(n.message))}}function Qn(t,e,n){let s=u.tables.get(t);if(!s)return;u.tables.set(t,Re({...s,version:s.version+1,rows:e,...n?{columns:n}:{}}));for(let i of[...xt])i([t]);let r=document.querySelector(`[data-testid="table"][data-table="${t}"]`);r&&(r.textContent=ss(u.tables.get(t)));let o=document.querySelector(`textarea[data-rows="${t}"]`);o&&document.activeElement!==o&&(o.value=JSON.stringify(e,null,2))}async function ti(){let t=await fetch("/config.json").then(n=>n.json()),e=!1;for(let[n,s]of Object.entries(t.tables)){if(kt.get(n)===s)continue;let r=Ue(n);if(!u.tables.has(r)){Tt(n,s),e=!0;continue}kt.set(n,s);try{let o=Gn(n,s);ge.delete(n),Qn(r,o.rows,o.columns)}catch(o){ge.set(n,String(o.message)),te()}}e&&(Pt(),te(),await ye())}function Pt(){let t=[...u.tables.keys()][0];if(!(!t||!T))for(let[e,n]of Object.entries(T.params??{}))n.type==="datatable"&&!u.props[e]&&(u.props[e]=t,u.touched.add(e))}function ni(){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 z.people.add(t),()=>{z.people.delete(t)}}}}function wt(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 z.people)s([n]);O("people-count").textContent=`${u.people.length} in view`}var si={status:()=>E.controller.status,on(t,e){let n=z.player.get(t)??new Set;return n.add(e),z.player.set(t,n),()=>{n.delete(e)}},async call(t,e,n){return be.push(`controller: ${n.app??"the app"} called ${t}, not applied (a preview)`),Y({}),{ok:!1,code:"PREVIEW",message:"a preview"}}},ri={time:"time.now",play:"player.play",pause:"player.pause",stop:"player.stop",next:"player.next",previous:"player.previous",finish:"app.finish"};function ii(t){let e=s=>r=>{throw be.push(`controller: ${s} refused, ${r.code??"error"}: ${r.message}`),Y({}),r},n={...t,call:(s,r)=>t.call(s,r).catch(e(s))};for(let[s,r]of Object.entries(ri))n[s]=()=>t[s]().catch(e(r));return n}function es(t){oi(t);for(let e of[t.type,"*"])for(let n of z.player.get(e)??[])n(t)}function oi(t){let e=vt??{};switch(t.type){case"state.changed":e.state=t.state;break;case"connection.changed":e.connection=t.connection;break;case"audio.changed":e.audio=t.audio;break;case"display.changed":e.display=t.display;break;case"item.start":e.now=[{zone:t.zone,item:t.item,blob:t.blob,contentType:t.contentType,startedAt:t.startedAt,...t.durationMs===void 0?{}:{durationMs:t.durationMs}}];break;case"item.end":e.now=(e.now??[]).filter(n=>n.item.id!==t.item.id);break}}function ai(){let t=O("command-name").value.trim(),e=O("command-argument").value.trim(),n;try{n=e?JSON.parse(e):void 0}catch{return}t&&(be.push(`controller: the command ${t} sent to the app`),Y({}),es({type:"app.command",seq:0,at:new Date().toISOString(),name:t,...n?{argument:n}:{},by:{kind:"remote",principal:"user:preview"}}))}function li(){return new $e({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 di(){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:fe-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:Zr,props:u.props,...T.slots&&u.tables.size?{binding:{table:[...u.tables.keys()][0]}}:{},...T.capabilities?.sensors?{sensors:T.capabilities.sensors}:{},appInfo:{id:T.id,element:T.element,player:T.capabilities?.player==="control"?"control":"read"}}],animation:{duration:{kind:"fixed",ms:fe},clips:t}}}async function ye(){let t=++me;if(cancelAnimationFrame(He),W?.dispose(),W=null,St.replaceChildren(),Be.length=0,be.length=0,j.last=null,j.reads=[],Et={kinds:{},nodes:0},!T||!Mt){Y({building:!0});return}let e={};try{e=JSON.parse(u.screen.fields||"{}")}catch{}let n=Mt,s={deviceId:"preview",slide:()=>null,playlist:()=>null,font:()=>{},app:()=>({hash:`preview-${me}`,bytes:n}),data:li(),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,onSnapshots:a=>(xt.add(a),()=>{xt.delete(a)}),...E.frames?{}:{...T.capabilities?.sensors?.includes("impressions")?{impressions:ni()}:{},controllerFor:a=>ii(Un(si,a))}};z.people.clear(),z.player.clear();let r=performance.now(),o=!1;Fe++;let i=new Q(di(),s,St,{appDeadlineMs:Jn,...E.frames?{isolate:{frames:E.frames,apps:()=>!0}}:{},onAppLate:()=>{o=!0},onApp:a=>{t===me&&(Be.push(`app.failed: ${a.reason}${a.error?` (${a.error})`:""}`),Y({}))}});W=i,Y({waiting:!0}),await i.ready,t===me&&(Y({readyMs:o?void 0:Math.round(performance.now()-r),late:o}),await i.start(Date.now()-u.t),t===me&&(i.seek(u.t),ee={wall:Date.now(),t:u.t},pi(),ci()))}function pi(){cancelAnimationFrame(He);let t=()=>{if(W){if(u.playing){if(u.t=ee.t+(Date.now()-ee.wall)*u.speed,u.t>=fe){u.t=0,ye();return}W.seek(u.t)}O("clock-seek").value=String(Math.round(u.t)),O("clock-time").textContent=`${(u.t/1e3).toFixed(1)} s`,He=requestAnimationFrame(t)}};He=requestAnimationFrame(t)}function ci(){let t=O("pages"),e=W?.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:()=>ts(s*e.ms)},`Page ${s+1}`)))):g("p",{"data-testid":"pages"},"One page"))}function ts(t){u.t=Math.max(0,Math.min(fe-1,t)),ee={wall:Date.now(),t:u.t},W?.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 ${Jn/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=zn.querySelector('[data-testid="ready"]');n&&e.push(n)}for(let n of Be)e.push(g("span",{class:"failed","data-testid":"failure"},n));E.frames&&Be.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 be.slice(-5))e.push(g("span",{"data-testid":"command"},n));Fe&&e.push(g("span",{"data-testid":"made-times"},`Made ${Fe===1?"once":`${Fe} times`}: at each start of its layout, and when its code, params or screen change. A table's change reaches it as it plays.`)),zn.replaceChildren(...e)}function ui(){let t=E.frames?Et:Xr();if(j.last&&u.playing&&W){let i={};for(let[a,l]of Object.entries(t.kinds)){let p=l-(j.last.kinds[a]??0);p>0&&(i[a]=p)}j.reads=[...j.reads,i].slice(-Vn)}j.last=t;let e=j.reads.length===Vn?Object.keys(yt).filter(i=>j.reads.every(a=>a[i])):[],n=i=>Math.round(j.reads.reduce((a,l)=>a+(l[i]??0),0)*1e3/(Xn*j.reads.length)),s=e.map(i=>[i,`Makes ${n(i)} ${yt[i]} a second while it plays. ${i==="listener"?"Add a listener once, in mount(), and take it off when the app goes":"Make it once, in mount() or with useMemo, never in draw() or a render"}: one made at every frame holds memory a screen frees late, and can run it out.`]),r=`${e.length?"":"Nothing made at every frame. "}${t.nodes} DOM nodes in the app.`,o=JSON.stringify(s);if(o===j.said&&bt.firstChild){bt.querySelector('[data-testid="made-counts"]').textContent=r;return}j.said=o,bt.replaceChildren(...s.map(([i,a])=>g("span",{class:"warn","data-testid":"made","data-kind":i},a)),g("span",{"data-testid":"made-counts"},r))}function hi(){let t=Object.entries(T?.params??{}).find(([,n])=>n.type==="datatable")?.[0],e=T?.slots?[...u.tables.keys()][0]:t?u.props[t]:void 0;return typeof e=="string"?u.tables.get(e):void 0}var ns=t=>t.label&&t.label!==t.key?`${t.label} (${t.key})`:t.key,ss=t=>`${t.table}: ${t.rows.length} rows, ${t.columns.map(ns).join(", ")}`;function mi(t,e){let n=u.props[t],s=o=>{u.props[t]=o,u.touched.add(t),rs(),R()},r=e.label??t;if(e.type==="boolean")return S(r,g("input",{type:"checkbox",checked:n===!0,onchange:o=>s(o.target.checked)}),!0);if(e.type==="enum"){let o=(e.values??[]).map(i=>i&&typeof i=="object"?[String(i.value),String(i.label??i.value)]:String(i));return S(r,q(o,String(n??""),s))}if(e.type==="number")return S(r,g("input",{type:"number",value:n===void 0?"":String(n),min:e.min,max:e.max,step:e.step??"any",onchange:o=>{let i=o.target.value;s(i===""?void 0:Number(i))}}));if(["string","text","color","url","datetime","duration","secret"].includes(e.type)){let o=e.type==="text"?g("textarea",{value:String(n??""),onchange:i=>s(i.target.value)}):g("input",{type:"text",value:String(n??""),placeholder:e.type==="color"?"#rrggbb or #rrggbbaa":void 0,onchange:i=>s(i.target.value)});return S(r,o)}return e.type==="datatable"?S(r,q(["",...u.tables.keys()],String(n??""),s)):e.type==="column"?S(r,q(["",...(hi()?.columns??[]).map(o=>[o.key,ns(o)])],String(n??""),s)):S(`${r} (JSON)`,g("textarea",{value:n===void 0?"":JSON.stringify(n,null,2),onchange:o=>{let i=o.target.value.trim();try{s(i?JSON.parse(i):void 0)}catch{s(i)}}}))}function rs(){let t=O("params"),e=T?.params??{},n=new Set(Hn(e,u.props));t.replaceChildren(...Object.entries(e).filter(([s])=>n.has(s)).map(([s,r])=>mi(s,r))),t.children.length||t.append(g("p",{},"No params."))}var Zn;function R(){clearTimeout(Zn),Zn=setTimeout(()=>{ye()},150)}function te(){let t=!je.some(([,i,a])=>i===u.width&&a===u.height),e=g("input",{type:"number",value:String(u.width),min:64,max:7680,onchange:i=>{u.width=Number(i.target.value)||1920,R()}}),n=g("input",{type:"number",value:String(u.height),min:64,max:7680,onchange:i=>{u.height=Number(i.target.value)||1080,R()}}),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)),ee={wall:Date.now(),t:u.t}}},u.playing?"Pause":"Play"),r=g("input",{id:"clock-seek",type:"range",min:0,max:fe,step:100,value:String(u.t),oninput:i=>ts(Number(i.target.value))}),o=[g("h2",{},"Screen"),S("Size",q([...je.map(([i])=>i),"Custom"],t?"Custom":je.find(([,i,a])=>i===u.width&&a===u.height)[0],i=>{let a=je.find(([l])=>l===i);a&&(u.width=a[1],u.height=a[2]),te(),R()})),t?g("div",{class:"row"},S("Width",e),S("Height",n)):null,g("div",{class:"row"},S("Time zone",g("input",{type:"text",value:u.timeZone,onchange:i=>{u.timeZone=i.target.value||"UTC",R()}})),S("Locale",g("input",{type:"text",value:u.locale,onchange:i=>{u.locale=i.target.value||"en-US",R()}}))),S("Screen's name",g("input",{type:"text",value:u.screen.name,onchange:i=>{u.screen.name=i.target.value,R()}})),g("div",{class:"row"},S("Latitude",g("input",{type:"number",step:"any",value:String(u.screen.lat),onchange:i=>{u.screen.lat=Number(i.target.value),R()}})),S("Longitude",g("input",{type:"number",step:"any",value:String(u.screen.lon),onchange:i=>{u.screen.lon=Number(i.target.value),R()}}))),S("Screen's fields (JSON)",g("textarea",{value:u.screen.fields,onchange:i=>{u.screen.fields=i.target.value,R()}})),g("h2",{},"Clock"),g("div",{class:"row"},s,S("Speed",q(Kr.map(i=>[String(i),`${i}\xD7`]),String(u.speed),i=>{ee={wall:Date.now(),t:u.t},u.speed=Number(i)})),g("span",{id:"clock-time"},"0.0 s")),S("Time in the layout",r),g("h2",{},"Enter and exit"),g("div",{class:"row"},S("Enter",q(Wn,u.enter,i=>{u.enter=i,R()})),S("Exit",q(Wn,u.exit,i=>{u.exit=i,R()}))),S("Each lasts (ms)",g("input",{type:"number",min:0,max:5e3,step:100,value:String(u.clipMs),onchange:i=>{u.clipMs=Number(i.target.value)||0,R()}})),g("div",{id:"pages"}),g("h2",{},"Params"),g("div",{id:"params"}),E.frames?g("p",{"data-testid":"framed"},`In a frame on ${E.frames}, as in the CMS: no storage, cookies, parent page, controller or impressions.`):null,g("h2",{},"Tables"),...[...u.tables.values()].flatMap(i=>[g("p",{"data-testid":"table","data-table":i.table},ss(i)),S(`${i.table}'s rows (JSON)`,g("textarea",{"data-rows":i.table,value:JSON.stringify(i.rows,null,2),rows:6,onchange:a=>{try{let l=JSON.parse(a.target.value);Array.isArray(l)&&Qn(i.table,l)}catch{}}}))]),...[...ge].map(([i,a])=>g("p",{class:"failed","data-testid":"table-error"},`${i}: ${a}`)),S("Add a table (JSON or CSV)",g("input",{type:"file",accept:".json,.csv",onchange:async i=>{let a=i.target.files?.[0];a&&(Tt(a.name,await a.text()),Pt(),te(),R())}})),...T?.capabilities?.sensors?.includes("impressions")&&!E.frames?[g("h2",{},"People at the screen"),g("div",{class:"row"},g("button",{type:"button",onclick:()=>wt("enter")},"Someone arrives"),g("button",{type:"button",onclick:()=>wt("look")},"Someone looks"),g("button",{type:"button",onclick:()=>wt("leave")},"Someone leaves")),g("p",{id:"people-count"},`${u.people.length} in view`)]:[],...E.frames?[]:[g("h2",{},"The player's controller"),g("p",{},`The app may ${T?.capabilities?.player==="control"?"read it and call its commands":"read it (capabilities.player: read)"}. Commands are not applied here.`),...Object.keys(E.controller.events).length?[S("Event",q(Object.keys(E.controller.events),Object.keys(E.controller.events)[0],i=>{O("event-json").value=JSON.stringify(E.controller.events[i],null,2)})),S("Its JSON",g("textarea",{id:"event-json",rows:6,value:JSON.stringify(Object.values(E.controller.events)[0],null,2)})),g("button",{type:"button","data-testid":"send-event",onclick:()=>{try{es(JSON.parse(O("event-json").value))}catch{}}},"Send to the app")]:[],S("A command's name",g("input",{id:"command-name",value:"showSpecial"})),S("Its argument (JSON, or nothing)",g("textarea",{id:"command-argument",rows:3,value:JSON.stringify({sku:"1234"})})),g("button",{type:"button","data-testid":"send-command",onclick:ai},"Send the command")],g("h2",{},"Data"),...Object.keys(u.samples).map(i=>S(`${i} answer's data (JSON)`,g("textarea",{"data-source":i,value:JSON.stringify(u.samples[i],null,2),rows:8,onchange:a=>{try{u.samples[i]=JSON.parse(a.target.value),R()}catch{}}}))),Object.keys(u.samples).length?null:g("p",{},"The app declares no data source with a sample (weather, news or calendar).")];Yr.replaceChildren(...o.filter(i=>i!==null)),rs()}async function fi(){await Kn(),te(),setInterval(ui,Xn),await ye();let t=new EventSource("/events"),e=()=>{Kn().then(()=>(te(),ye()))};t.addEventListener("hello",n=>{JSON.parse(n.data).version!==Yn&&e()}),t.addEventListener("reload",e),t.addEventListener("tables",()=>{ti()}),window.addEventListener("resize",()=>R())}fi();})();
package/dist/react.d.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { type ComponentType } from "react";
10
10
  import type { DataAnswer, ScreenFacts } from "./contract/app-contract.js";
11
+ import { type Followed } from "./following.js";
11
12
  type Params = Record<string, string | number | boolean>;
12
13
  /**
13
14
  * An element class for a component, the module's default export: `export default reactApp(Board, { css })`. `css` is
@@ -60,4 +61,17 @@ export declare function useData<T = unknown>(source: string, params: Params, eve
60
61
  * every frame, as before.
61
62
  */
62
63
  export declare function useSnapshot(table: string): unknown | null;
64
+ /** The player's state: starting, playing, paused, stopped, idle or error. Null where the app has no controller (the designer). */
65
+ export declare function usePlayback(): Followed<"playback">;
66
+ /** Whether the screen is online or offline; null without a controller. */
67
+ export declare function useConnection(): Followed<"connection">;
68
+ /** The screen's volume and whether it is muted; null without a controller, or where the host sets no audio. */
69
+ export declare function useAudio(): Followed<"audio">;
70
+ /** The items playing (the status's `now`); null without a controller. */
71
+ export declare function usePlaying(): Followed<"now">;
72
+ /**
73
+ * Calls `handler` with the argument of each command of this name sent to the screen's apps (`apps.sendCommand`,
74
+ * WP-0150 decision 3), from mount until the component goes. The handler may change between renders: the latest is called.
75
+ */
76
+ export declare function useCommand(name: string, handler: (argument: Record<string, unknown> | undefined) => void): void;
63
77
  export {};
package/dist/react.js CHANGED
@@ -17,8 +17,10 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
17
17
  * React's tree (which hangs off the shadow root's nodes) holds nothing another app could use: data, images and the
18
18
  * rest go through the SDK's functions, as for any app.
19
19
  */
20
- import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
20
+ import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
21
21
  import { createRoot } from "react-dom/client";
22
+ import { follow } from "./following.js";
23
+ import { onCommand } from "./commands.js";
22
24
  import { contextOf, data, kept, snapshot, WyndlassApp } from "./index.js";
23
25
  const bridges = new WeakMap();
24
26
  const App = createContext(null);
@@ -169,3 +171,27 @@ export function useSnapshot(table) {
169
171
  useSyncExternalStore(subscribe, read, read);
170
172
  return snapshot(app, table);
171
173
  }
174
+ /** A field of the player's status (WP-0150 decision 2): the component renders when its value changes, not at frames. */
175
+ function useFollowed(field) {
176
+ const [app] = useBridge();
177
+ const f = useMemo(() => follow(app, field), [app, field]);
178
+ return useSyncExternalStore(f.subscribe, f.get, f.get);
179
+ }
180
+ /** The player's state: starting, playing, paused, stopped, idle or error. Null where the app has no controller (the designer). */
181
+ export function usePlayback() { return useFollowed("playback"); }
182
+ /** Whether the screen is online or offline; null without a controller. */
183
+ export function useConnection() { return useFollowed("connection"); }
184
+ /** The screen's volume and whether it is muted; null without a controller, or where the host sets no audio. */
185
+ export function useAudio() { return useFollowed("audio"); }
186
+ /** The items playing (the status's `now`); null without a controller. */
187
+ export function usePlaying() { return useFollowed("now"); }
188
+ /**
189
+ * Calls `handler` with the argument of each command of this name sent to the screen's apps (`apps.sendCommand`,
190
+ * WP-0150 decision 3), from mount until the component goes. The handler may change between renders: the latest is called.
191
+ */
192
+ export function useCommand(name, handler) {
193
+ const [app] = useBridge();
194
+ const latest = useRef(handler);
195
+ latest.current = handler;
196
+ useEffect(() => onCommand(app, name, argument => latest.current(argument)), [app, name]);
197
+ }
package/dist/signals.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  /**
2
- * Table rows as Preact signals (WP-0149 decision 5, `@wyndlass/app-sdk/signals`): for an app that updates its DOM when its
3
- * rows change, in an effect, rather than at every frame. An entry of its own, so an app that does not import it does not
4
- * bundle it. Its signals are this copy of `@preact/signals-core`'s: take effect, computed and the rest from here too, as
5
- * an effect from another copy does not see them.
2
+ * Table rows and the player's state as Preact signals (WP-0149 decision 5, WP-0150 decision 1;
3
+ * `@wyndlass/app-sdk/signals`): for an app that updates its DOM when they change, in an effect, rather than at every
4
+ * frame. An entry of its own, so an app that does not import it does not bundle it. Its signals are this copy of
5
+ * `@preact/signals-core`'s: take effect, computed and the rest from here too, as an effect from another copy does not
6
+ * see them.
6
7
  */
7
8
  import { type ReadonlySignal } from "@preact/signals-core";
9
+ import type { PlayerAPIStatus } from "./contract/player-api-status.js";
8
10
  import { type WyndlassApp } from "./index.js";
9
11
  export { batch, computed, effect, signal, untracked, type ReadonlySignal, type Signal } from "@preact/signals-core";
10
12
  /**
@@ -15,3 +17,17 @@ export { batch, computed, effect, signal, untracked, type ReadonlySignal, type S
15
17
  * On a player before WP-0149, which makes the app again when a table changes, it keeps the rows the app was made with.
16
18
  */
17
19
  export declare function tableSignal(app: WyndlassApp, table: string): ReadonlySignal<unknown | null>;
20
+ /**
21
+ * The player's state as a read-only signal: starting, playing, paused, stopped, idle or error, set on each
22
+ * `state.changed`. Null where the app has no controller (the designer). Make it in mount(), where the app has its context.
23
+ */
24
+ export declare function playbackSignal(app: WyndlassApp): ReadonlySignal<PlayerAPIStatus["state"] | null>;
25
+ /** Whether the screen is online or offline, as a read-only signal set on each `connection.changed`; null without a controller. */
26
+ export declare function connectionSignal(app: WyndlassApp): ReadonlySignal<PlayerAPIStatus["connection"] | null>;
27
+ /**
28
+ * The screen's volume and whether it is muted, as a read-only signal set on each `audio.changed`. Null without a
29
+ * controller, or on a screen whose host sets no audio.
30
+ */
31
+ export declare function audioSignal(app: WyndlassApp): ReadonlySignal<NonNullable<PlayerAPIStatus["audio"]> | null>;
32
+ /** The items playing (the status's `now`), as a read-only signal read again on each `item.start` and `item.end`; null without a controller. */
33
+ export declare function nowSignal(app: WyndlassApp): ReadonlySignal<PlayerAPIStatus["now"] | null>;
package/dist/signals.js CHANGED
@@ -1,10 +1,12 @@
1
1
  /**
2
- * Table rows as Preact signals (WP-0149 decision 5, `@wyndlass/app-sdk/signals`): for an app that updates its DOM when its
3
- * rows change, in an effect, rather than at every frame. An entry of its own, so an app that does not import it does not
4
- * bundle it. Its signals are this copy of `@preact/signals-core`'s: take effect, computed and the rest from here too, as
5
- * an effect from another copy does not see them.
2
+ * Table rows and the player's state as Preact signals (WP-0149 decision 5, WP-0150 decision 1;
3
+ * `@wyndlass/app-sdk/signals`): for an app that updates its DOM when they change, in an effect, rather than at every
4
+ * frame. An entry of its own, so an app that does not import it does not bundle it. Its signals are this copy of
5
+ * `@preact/signals-core`'s: take effect, computed and the rest from here too, as an effect from another copy does not
6
+ * see them.
6
7
  */
7
8
  import { computed, signal } from "@preact/signals-core";
9
+ import { follow } from "./following.js";
8
10
  import { eachFrame } from "./frames.js";
9
11
  import { contextOf, snapshot } from "./index.js";
10
12
  export { batch, computed, effect, signal, untracked } from "@preact/signals-core";
@@ -28,3 +30,32 @@ export function tableSignal(app, table) {
28
30
  }
29
31
  return computed(() => rows.value);
30
32
  }
33
+ /** A field of the player's status as a read-only signal (WP-0150 decision 1): set only when its value changes. */
34
+ function followed(app, field) {
35
+ const f = follow(app, field);
36
+ const value = signal(f.get());
37
+ f.subscribe(() => { value.value = f.get(); });
38
+ return computed(() => value.value);
39
+ }
40
+ /**
41
+ * The player's state as a read-only signal: starting, playing, paused, stopped, idle or error, set on each
42
+ * `state.changed`. Null where the app has no controller (the designer). Make it in mount(), where the app has its context.
43
+ */
44
+ export function playbackSignal(app) {
45
+ return followed(app, "playback");
46
+ }
47
+ /** Whether the screen is online or offline, as a read-only signal set on each `connection.changed`; null without a controller. */
48
+ export function connectionSignal(app) {
49
+ return followed(app, "connection");
50
+ }
51
+ /**
52
+ * The screen's volume and whether it is muted, as a read-only signal set on each `audio.changed`. Null without a
53
+ * controller, or on a screen whose host sets no audio.
54
+ */
55
+ export function audioSignal(app) {
56
+ return followed(app, "audio");
57
+ }
58
+ /** The items playing (the status's `now`), as a read-only signal read again on each `item.start` and `item.end`; null without a controller. */
59
+ export function nowSignal(app) {
60
+ return followed(app, "now");
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wyndlass/app-sdk",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "Build apps for Wyndlass's screens: web components on the app contract (Player API 2), with the runtime Wyndlass's own apps use.",
5
5
  "keywords": [
6
6
  "wyndlass",
@@ -28,7 +28,8 @@ A paged app also defines `paging()`, returning `{count, ms}`, so the layout can
28
28
  - `kept(this, source, params)`: the answer the screen last kept for the same request, at once, or `undefined`. Draw it while `data` asks again, so the app is ready without the network;
29
29
  - `image(this, path)`: a picture a data answer names, as a URL the app can draw;
30
30
  - `snapshot(this, table)`: the rows of a table the layout names, in a `datatable` param or in the element's `binding`. Where a binding chooses rows (a filter, a sort, a limit), only those. `null` when the screen has no snapshot of that table. A `datatable` param needs no binding. A screen gives the app a table's new rows while it plays: read them where you draw (in `draw()`, `useSnapshot` or `tableSignal`), never once in `mount()`. They are the same object until they change, and frozen: copy rows before you sort or change them;
31
- - `controllerOf(this)`: the player's controller, as `capabilities.player` allows; absent in the designer and previews;
31
+ - `controllerOf(this)`: the player's controller, as `capabilities.player` allows; absent in the designer and the CMS's previews (`wyndlass-app preview` gives a stand-in). Follow its state with the signals or hooks below, never by reading `status()` at each frame;
32
+ - `onCommand(this, name, handler)`: each command of that name sent to the screen's apps (`apps.sendCommand`), with its argument; returns how to stop. A command is an event: the same one sent twice runs twice. Apps cannot answer one;
32
33
  - `impressionsOf(this)`: the people at the screen, for an app whose definition declares the `impressions` sensor;
33
34
  - `contextOf(this)`: the rest of the context (the screen's `place` and facts, `preview`).
34
35
 
@@ -43,12 +44,15 @@ A paged app also defines `paging()`, returning `{count, ms}`, so the layout can
43
44
  - `useNow(everyMs?)`: the layout's clock. Without `everyMs`, drawn again at each of its frames, so the component renders many times a second. With it, the time is a multiple of `everyMs` (`useNow(1000)`: whole seconds), and the component renders only when that changes: take the coarsest the app shows. Either way, make formatters and other objects with `useMemo`, never in the render;
44
45
  - `usePaging(count, ms)`: the page showing now, rendering when it turns. It also tells the host the pages;
45
46
  - `useData(source, params)`: `{answer, error}`. The kept answer at once, then the service's, asked again every 10 minutes;
46
- - `useSnapshot(table)`: a data table's rows, as `snapshot` gives them. The component renders when they change.
47
+ - `useSnapshot(table)`: a data table's rows, as `snapshot` gives them. The component renders when they change;
48
+ - `usePlayback()`, `useConnection()`, `useAudio()`, `usePlaying()`: the player's state (starting, playing, paused, stopped, idle, error), online or offline, `{volume, muted}`, and the items playing. Each renders only when its value changes; `null` without a controller;
49
+ - `useCommand(name, handler)`: each command of that name, with its argument, while the component is mounted.
47
50
 
48
51
  ## Signals (`@wyndlass/app-sdk/signals`)
49
52
 
50
- For a vanilla app that updates its DOM when its rows change, rather than at every frame:
53
+ For a vanilla app that updates its DOM when its rows or the player's state change, rather than at every frame:
51
54
  - `tableSignal(this, table)`: a read-only Preact signal of the table's snapshot, made in `mount()`. Its value changes when the rows do;
55
+ - `playbackSignal(this)`, `connectionSignal(this)`, `audioSignal(this)`, `nowSignal(this)`: the player's state, online or offline, `{volume, muted}`, and the items playing, as read-only signals made in `mount()`. Each is set only when its value changes; `null` without a controller;
52
56
  - `effect`, `computed`, `batch`, `signal`, `untracked`: Preact's, from the same copy. Import them from here, never from `@preact/signals-core`, whose effects would not see these signals.
53
57
 
54
58
  Draw in an `effect(() => { … rows.value … })` made in `mount()`, and leave `draw()` empty.
@@ -6,6 +6,7 @@ An app for Wyndlass's screens, on `@wyndlass/app-sdk` and React. Its README says
6
6
  - **Time:** `useNow()` gives the layout's clock, drawn again at each of its frames, and `useNow(1000)` whole seconds, rendering once a second: take the coarsest the app shows; never `Date.now()`, so a seek or a pause shows the right moment.
7
7
  - **Nothing made in a render:** the component renders at every frame, many times a second. Make formatters (`Intl.DateTimeFormat`, `Intl.NumberFormat`) and other objects with `useMemo` on what they depend on, never in the render. One made in each draw holds memory a screen does not free in time: an app that did so ran a 4 GB screen out of memory within an hour.
8
8
  - **A table's rows from `useSnapshot(table)`:** a screen gives the app a table's new rows while it plays, and the component renders when they change. Never keep a copy made once. They are frozen: copy before you sort or change them.
9
+ - **The player's state** (playing or paused, online or offline, its sound): `usePlayback()`, `useConnection()` and `useAudio()`, which render only when it changes. **Commands** sent to the screen's apps: `useCommand(name, handler)`. A command is an event, and the app cannot answer it.
9
10
  - **Ready:** call `useApp().ready()` once drawn, within 5 s, or the screen reports the app late. `useData()` gives the answer the screen kept at once, then the service's.
10
11
  - **Data:** only through `useData(source, params)`, and only the sources `app.json`'s `data` lists. Pages: `usePaging(count, ms)`.
11
12
  - **Styles:** `src/app.css`, passed as `css`, never a `<style>`.
@@ -6,6 +6,7 @@ An app for Wyndlass's screens, on `@wyndlass/app-sdk`. Its README says what it i
6
6
  - **Time:** from `now()` or `draw`'s `epochMs`, never `Date.now()`, so a seek or a pause shows the right moment.
7
7
  - **Nothing made in `draw`:** it runs at every frame, many times a second. Make formatters (`Intl.DateTimeFormat`, `Intl.NumberFormat`) and other objects in `mount()`, never in `draw`. One made in each draw holds memory a screen does not free in time: an app that did so ran a 4 GB screen out of memory within an hour.
8
8
  - **A table's rows where you draw:** a screen gives the app a table's new rows while it plays. Read them with `snapshot(this, table)` in `draw`, or with `tableSignal(this, table)` and an `effect` from `@wyndlass/app-sdk/signals`, made in `mount()`. Never read them once in `mount()` and keep them. They are frozen: copy before you sort or change them.
9
+ - **The player's state** (playing or paused, online or offline, its sound): `playbackSignal(this)`, `connectionSignal(this)` and `audioSignal(this)` from `@wyndlass/app-sdk/signals`, made in `mount()`, never `status()` read at each frame. **Commands** sent to the screen's apps: `onCommand(this, name, handler)`. A command is an event, and the app cannot answer it.
9
10
  - **Ready:** call `ready()` once drawn, within 5 s, or the screen reports the app late. Draw the answer `kept()` gives at once, then ask `data()`.
10
11
  - **Data:** only through `data(this, source, params)`, and only the sources `app.json`'s `data` lists. Images a data answer names through `image(this, path)`.
11
12
  - **Styles:** `this.css(text)`, never a `<style>`. A CSS file the source imports is its text.