@wyndlass/app-sdk 1.1.2 → 1.2.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 +7 -5
- package/cli/preview.mjs +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/preview/frame/index.html +1 -0
- package/dist/preview/frame/made.js +2 -0
- package/dist/preview/index.html +5 -4
- package/dist/preview/page.js +5 -5
- package/dist/react.d.ts +9 -3
- package/dist/react.js +15 -7
- package/package.json +1 -1
- package/skill/wyndlass-app/SKILL.md +3 -0
- package/skill/wyndlass-app/references/contract.md +3 -3
- package/skill/wyndlass-app/references/templates.md +1 -1
- package/templates/react/AGENTS.md +3 -2
- package/templates/react/src/app.tsx +3 -1
- package/templates/vanilla/AGENTS.md +2 -1
package/README.md
CHANGED
|
@@ -67,6 +67,8 @@ Its controls:
|
|
|
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
|
|
|
70
|
+
**What it makes while it plays** is shown below that: anything it makes at every frame that holds memory outside the script's heap, such as date and number formatters (`Intl.DateTimeFormat`, or `toLocaleTimeString` with options), canvases, object URLs, workers and listeners on `window`, with what to do; and how many DOM nodes it has. One made at every frame holds memory a screen frees late, and can run it out. Fix what it says before you pack.
|
|
71
|
+
|
|
70
72
|
**`npm run preview -- --frame`** runs the app in the frame the CMS gives every imported app: sandboxed, on an origin of its own, with no storage, cookies or parent page. An app that reaches for one of them works on screens but fails in the CMS's designer. Run it with `--frame` before you import.
|
|
71
73
|
|
|
72
74
|
## Check and pack it
|
|
@@ -135,7 +137,7 @@ export default class QueueBoard extends WyndlassApp {
|
|
|
135
137
|
}
|
|
136
138
|
```
|
|
137
139
|
|
|
138
|
-
- **`WyndlassApp`:** extend it, and write `mount()` (build the shadow DOM, once) and `draw(epochMs)` (draw that moment of the layout's clock). It gives:
|
|
140
|
+
- **`WyndlassApp`:** extend it, and write `mount()` (build the shadow DOM, once) and `draw(epochMs)` (draw that moment of the layout's clock, at every frame: make formatters and other objects in `mount()`, never in `draw`). It gives:
|
|
139
141
|
- `prop(name, default)`, `font()` (the font param as CSS), `now()`, `startedAt()`, `timeZone` and `locale`;
|
|
140
142
|
- `css(text)`: styles for the shadow root, as a constructable stylesheet;
|
|
141
143
|
- `ready()`: says the app may be shown, once, within 5 s of its start;
|
|
@@ -165,7 +167,7 @@ import css from "./app.css";
|
|
|
165
167
|
|
|
166
168
|
function Board() {
|
|
167
169
|
const app = useApp();
|
|
168
|
-
const now = useNow();
|
|
170
|
+
const now = useNow(1000);
|
|
169
171
|
const page = usePaging(2, 8000);
|
|
170
172
|
const { answer, error } = useData("weather", { lat: 49.9, lon: -97.14 });
|
|
171
173
|
useEffect(() => { if (answer || error) app.ready(); }, [app, answer, error]);
|
|
@@ -177,10 +179,10 @@ export default reactApp(Board, { css });
|
|
|
177
179
|
|
|
178
180
|
- **`reactApp(Component, { css })`** makes the element's class, the module's default export. The component renders in the app's shadow root, with `css` as its styles. React is a peer dependency, and `pack` bundles it.
|
|
179
181
|
- **`useApp()`:** `prop(name, default)`, `ready()`, `timeZone`, `locale`, the screen's `place` and `screen`, and `preview`.
|
|
180
|
-
- **`useNow()`:** the layout's clock
|
|
181
|
-
- **`usePaging(count, ms)`:** the page showing now. It also tells the host the pages.
|
|
182
|
+
- **`useNow(everyMs?)`:** the layout's clock. Without `everyMs`, the component renders again at each of the layout's frames, 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 (`Intl.DateTimeFormat`, `Intl.NumberFormat`) and other objects with `useMemo`, 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.
|
|
183
|
+
- **`usePaging(count, ms)`:** the page showing now, rendering when it turns. It also tells the host the pages.
|
|
182
184
|
- **`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.
|
|
183
|
-
- **`useSnapshot(table)`:** a data table's snapshot.
|
|
185
|
+
- **`useSnapshot(table)`:** a data table's snapshot, read again at every frame, since a bound table's rows change with time.
|
|
184
186
|
|
|
185
187
|
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.
|
|
186
188
|
|
package/cli/preview.mjs
CHANGED
|
@@ -120,7 +120,7 @@ export async function preview(dir = ".", { port = 5199, data: dataDir, frame = f
|
|
|
120
120
|
// The frame's page and runtime only, nothing else, under the frames' policy: the app's document there is opaque.
|
|
121
121
|
frames = createServer((req, res) => {
|
|
122
122
|
const path = new URL(req.url ?? "/", "http://127.0.0.1").pathname;
|
|
123
|
-
const file = path === "/" ? "index.html" : path === "/frame.js" ? "frame.js" : null;
|
|
123
|
+
const file = path === "/" ? "index.html" : path === "/frame.js" ? "frame.js" : path === "/made.js" ? "made.js" : null;
|
|
124
124
|
if (!file) return res.writeHead(404).end();
|
|
125
125
|
res.writeHead(200, { "content-type": TYPES[file.endsWith(".js") ? ".js" : ".html"], "cache-control": "no-store", "content-security-policy": framePolicy(framesOrigin, pageOrigin) })
|
|
126
126
|
.end(readFileSync(join(page, "frame", file)));
|
package/dist/index.d.ts
CHANGED
|
@@ -72,7 +72,10 @@ export declare abstract class WyndlassApp extends HTMLElement {
|
|
|
72
72
|
protected ready(): void;
|
|
73
73
|
/** Builds the app's shadow DOM, once. */
|
|
74
74
|
protected abstract mount(): void;
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Draws the moment `epochMs` on the layout's clock, at every frame: make formatters and other objects in `mount()`,
|
|
77
|
+
* never here. One made per frame holds memory a screen does not free in time.
|
|
78
|
+
*/
|
|
76
79
|
protected abstract draw(epochMs: number): void;
|
|
77
80
|
update(epochMs: number): void;
|
|
78
81
|
enter(o: HookOptions): void;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
<!-- An app's frame in the CMS (WP-0100 decision 11). Its policy is the frames host's header (the CMS image), not a meta.
|
|
7
7
|
A classic script: this document's origin is opaque, so a module would be fetched with CORS. -->
|
|
8
8
|
<style>:root{color-scheme:normal}html,body,#app{margin:0;width:100%;height:100%;overflow:hidden;background:transparent}</style>
|
|
9
|
+
<script src="./made.js"></script>
|
|
9
10
|
<script src="./frame.js" defer></script>
|
|
10
11
|
</head>
|
|
11
12
|
<body><div id="app"></div></body>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
/* @wyndlass/app-sdk's preview: what an app makes, counted in its frame. MIT, (c) Northwynd Labs LLC. */
|
|
2
|
+
"use strict";(()=>{function g(n){let m=0,s=c=>{for(let o=c.firstChild;o;o=o.nextSibling){if(o.nodeType===1){m++;let u=o.shadowRoot;u&&s(u)}s(o)}};return s(n),m}function b(n,m){let s={},c=t=>{s[t]=(s[t]??0)+1},o=(t,e)=>new Proxy(t,{construct:(a,l,i)=>(c(e),Reflect.construct(a,l,i)),apply:(a,l,i)=>(c(e),Reflect.apply(a,l,i))}),u=n.Intl;for(let t of["DateTimeFormat","NumberFormat","Collator","PluralRules","RelativeTimeFormat","ListFormat","DisplayNames","Segmenter"]){let e=u[t];e&&(u[t]=o(e,`Intl.${t}`))}let r=(t,e,a)=>{let l=t&&t[e];typeof l=="function"&&(t[e]=function(...i){let p=Reflect.apply(l,this,i),k=a(i,this,p);return k&&c(k),p})},d=t=>t[1]!==void 0?"toLocale":null;for(let t of["toLocaleString","toLocaleDateString","toLocaleTimeString"])r(n.Date?.prototype,t,d);r(n.Number?.prototype,"toLocaleString",d),r(n.Document?.prototype,"createElement",t=>String(t[0]).toLowerCase()==="canvas"?"canvas":null),n.OffscreenCanvas&&(n.OffscreenCanvas=o(n.OffscreenCanvas,"canvas"));let f=new WeakSet,w=(t,e,a)=>!a||typeof a!="object"||f.has(a)?null:(f.add(a),"context");return r(n.HTMLCanvasElement?.prototype,"getContext",w),r(n.OffscreenCanvas?.prototype,"getContext",w),r(n,"createImageBitmap",()=>"bitmap"),r(n.URL,"createObjectURL",()=>"objectUrl"),n.Worker&&(n.Worker=o(n.Worker,"worker")),r(n.EventTarget?.prototype,"addEventListener",(t,e)=>e===void 0||e===n||e===n.document?"listener":null),()=>({kinds:{...s},nodes:m()})}var y=b(self,()=>g(document.getElementById("app")??document));setInterval(()=>parent.postMessage({wyndlassPreview:"made",made:y()},"*"),250);})();
|
package/dist/preview/index.html
CHANGED
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
:root { color-scheme: dark; --line: #2c3540; --muted: #93a1b0; --accent: #4f8cff; --danger: #ff6b6b; --ok: #4fd18b; --warn: #f5b84a; }
|
|
9
9
|
* { box-sizing: border-box; }
|
|
10
10
|
html, body { margin: 0; height: 100%; background: #12171d; color: #e6edf3; font: 14px/1.4 system-ui, sans-serif; }
|
|
11
|
-
body { display: grid; grid-template-columns: 330px 1fr; grid-template-rows: auto 1fr auto; height: 100vh; }
|
|
11
|
+
body { display: grid; grid-template-columns: 330px 1fr; grid-template-rows: auto 1fr auto auto; height: 100vh; }
|
|
12
12
|
header { grid-column: 1 / 3; display: flex; gap: 12px; align-items: baseline; padding: 10px 16px; border-bottom: 1px solid var(--line); }
|
|
13
13
|
header h1 { font-size: 16px; margin: 0; }
|
|
14
14
|
header span { color: var(--muted); }
|
|
15
|
-
#controls { grid-row: 2 /
|
|
15
|
+
#controls { grid-row: 2 / 5; overflow: auto; border-right: 1px solid var(--line); padding: 8px 14px 24px; }
|
|
16
16
|
#controls h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 18px 0 8px; }
|
|
17
17
|
.field { display: flex; flex-direction: column; gap: 3px; margin-bottom: 8px; }
|
|
18
18
|
.field.inline { flex-direction: row; align-items: center; gap: 8px; }
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
button { cursor: pointer; }
|
|
25
25
|
button[aria-pressed="true"] { border-color: var(--accent); color: var(--accent); }
|
|
26
26
|
#stage { position: relative; overflow: hidden; background: repeating-conic-gradient(#1a2028 0 25%, #151a21 0 50%) 0 0 / 24px 24px; }
|
|
27
|
-
#report { border-top: 1px solid var(--line); padding: 8px 16px; display: flex; flex-wrap: wrap; gap: 6px 18px; align-items: baseline; min-height: 40px; }
|
|
28
|
-
#report .ok { color: var(--ok); } #report .late, #report .warn { color: var(--warn); } #report .failed, #controls .failed { color: var(--danger); }
|
|
27
|
+
#report, #made { border-top: 1px solid var(--line); padding: 8px 16px; display: flex; flex-wrap: wrap; gap: 6px 18px; align-items: baseline; min-height: 40px; }
|
|
28
|
+
#report .ok { color: var(--ok); } #report .late, #report .warn, #made .warn { color: var(--warn); } #report .failed, #controls .failed { color: var(--danger); }
|
|
29
29
|
#report pre { margin: 0; white-space: pre-wrap; color: var(--danger); }
|
|
30
30
|
#clock-time { font-variant-numeric: tabular-nums; }
|
|
31
31
|
</style>
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
<aside id="controls" aria-label="Controls"></aside>
|
|
36
36
|
<section id="stage" aria-label="The app, as a screen shows it"></section>
|
|
37
37
|
<section id="report" aria-label="What a screen would report" aria-live="polite"></section>
|
|
38
|
+
<section id="made" aria-label="What it makes while it plays" aria-live="polite"></section>
|
|
38
39
|
<script src="/page.js"></script>
|
|
39
40
|
</body>
|
|
40
41
|
</html>
|
package/dist/preview/page.js
CHANGED
|
@@ -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 On=Object.defineProperty;var $n=(t,e,n)=>e in t?On(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var h=(t,e,n)=>$n(t,typeof e!="symbol"?e+"":e,n);var wt=Reflect.apply,Ne=Object.getOwnPropertyDescriptor,fe=Object.getPrototypeOf,R=globalThis,v=(t,e,n)=>wt(t,e,n),O=(t,e)=>t?Ne(t,e)?.get:void 0,Oe=(t,e)=>t?Ne(t,e)?.set:void 0,P=t=>R[t]?.prototype,vt=(t,e,n)=>wt(t,e,n);var ji=Date.now,Fi=new TextEncoder,Hi=new TextDecoder,Bi=TextEncoder.prototype.encode,Ui=TextDecoder.prototype.decode;var qi=O(fe(Uint8Array.prototype),"length");var Dn=Uint8Array,jn=t=>new Dn(t),Fn=[];for(let t=0;t<256;t++)Fn[t]=(t<16?"0":"")+t.toString(16);var Hn=Object.create(null);for(let t=0;t<64;t++)Hn["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[t]]=t;var lt=R.crypto?.subtle,$e=lt?fe(lt):void 0,Re=R.crypto,Bn=Re?fe(Re).getRandomValues:void 0,_i=$e?.digest,Wi=$e?.importKey,zi=$e?.sign;var kt=t=>v(Bn,Re,[jn(t)]),Vi={setTimeout:R.setTimeout,clearTimeout:R.clearTimeout,setInterval:R.setInterval,clearInterval:R.clearInterval};var Ki=R.fetch;var ne=P("MessagePort"),Zi=ne&&ne.postMessage,Ji=Oe(ne,"onmessage"),Yi=ne&&ne.start,De=P("MessageEvent"),Xi=O(De,"data"),Gi=O(De,"ports"),Qi=O(De,"source");var xt=P("EventTarget"),er=xt?.addEventListener,tr=xt?.removeEventListener;var nr=R.Worker,ue=P("Worker"),sr=ue&&ue.postMessage,ir=Oe(ue,"onmessage"),rr=Oe(ue,"onerror");var pt=R.Blob,he=R.URL,ct=he?.createObjectURL,Un=he?.revokeObjectURL,je=pt&&ct?{make:(t,e)=>v(ct,he,[new pt(t,{type:e})]),revoke:t=>v(Un,he,[t])}:void 0,ee=R.customElements,Fe=ee?fe(ee):void 0,qn=Fe?.define,_n=Fe?.get,He=ee&&Fe?{define:(t,e)=>v(qn,ee,[t,e]),get:t=>v(_n,ee,[t])}:void 0,At=R.document,me=P("Document"),Wn=me?.createElement,zn=me?.createElementNS,Vn=WeakSet.prototype.add,or=WeakSet.prototype.has,Be=new WeakSet,Kn=new WeakSet,ge=(t,e)=>(v(Vn,t,[e]),e);function D(t){return ge(Be,v(Wn,At,[t]))}var ye=t=>ge(Be,v(zn,At,["http://www.w3.org/2000/svg",t]));var K=t=>ge(Kn,t);var se=P("Node"),Ue=P("Element"),be=P("HTMLElement"),Z=t=>O(se,t),ut=Z("parentNode"),Zn=Z("firstChild"),Jn=Z("lastChild"),Yn=Z("nextSibling"),Xn=Z("nodeType"),Gn=Z("childNodes"),Qn=O(Ue,"localName"),es=se?.appendChild,ts=se?.insertBefore,ns=se?.removeChild,_=Ue,ss=_?.getAttribute,is=_?.getAttributeNames,rs=_?.setAttribute,os=_?.removeAttribute,as=_?.matches,ds=_?.attachShadow,ls=_?.querySelectorAll,ps=P("HTMLDialogElement")?.close,cs=O(P("NodeList"),"length"),us=P("NodeList")?.item,we=P("MutationRecord"),hs=O(we,"type"),ms=O(we,"target"),fs=O(we,"addedNodes"),gs=O(we,"removedNodes"),ht=R.MutationObserver,ys=P("MutationObserver")?.observe,Mt=me?Ne(me,"adoptedStyleSheets"):void 0,mt=Mt?.get,ft=Mt?.set,bs=R.requestAnimationFrame,gt=O(be,"style"),ve=P("CSSStyleDeclaration"),ws=ve?.getPropertyValue,vs=ve?.getPropertyPriority,ks=ve?.setProperty,yt=be?.showPopover,bt=be?.hidePopover,xs=O(P("Event"),"target"),As=P("Event")?.stopImmediatePropagation,ce=t=>{let e=[];for(let n=0,s=v(cs,t,[]);n<s;n++)e[n]=v(us,t,[n]);return e},te=ht&&se&&Ue&&be&&ve?{parent:t=>v(ut,t,[]),first:t=>v(Zn,t,[]),last:t=>v(Jn,t,[]),next:t=>v(Yn,t,[]),isElement:t=>v(Xn,t,[])===1,name:t=>v(Qn,t,[]),children:t=>ce(v(Gn,t,[])),append:(t,e)=>v(es,t,[e]),insert:(t,e,n)=>v(ts,t,[e,n]),remove:t=>{let e=v(ut,t,[]);e&&v(ns,e,[t])},attribute:(t,e)=>v(ss,t,[e]),attributes:t=>v(is,t,[]),setAttribute:(t,e,n)=>v(rs,t,[e,n]),removeAttribute:(t,e)=>v(os,t,[e]),matches:(t,e)=>v(as,t,[e]),all:(t,e)=>ce(v(ls,t,[e])),closeDialog:t=>v(ps,t,[]),shadow:t=>v(ds,t,[{mode:"closed"}]),style:(t,e)=>{let n=v(gt,t,[]);return[v(ws,n,[e]),v(vs,n,[e])]},setStyle:(t,e,n,s="")=>v(ks,v(gt,t,[]),[e,n,s]),observer:t=>new ht(t),observe:(t,e,n)=>v(ys,t,[e,n]),record:t=>({type:v(hs,t,[]),target:v(ms,t,[]),added:ce(v(fs,t,[])),removed:ce(v(gs,t,[]))}),adopted:mt&&ft?{count:t=>v(mt,t,[]).length,clear:t=>v(ft,t,[[]])}:void 0,frame:t=>v(bs,globalThis,[t]),popover:yt&&bt?{show:t=>v(yt,t,[]),hide:t=>v(bt,t,[])}:void 0,target:t=>v(xs,t,[]),stop:t=>v(As,t,[])}:void 0;function qe(t){if(ge(Be,t),te)for(let e of te.children(t))te.isElement(e)&&qe(e);return t}var _e=t=>new TextEncoder().encode(t);function Ms(t){let e=t instanceof Uint8Array?t:new Uint8Array(t),n="";for(let s of e)n+=s.toString(16).padStart(2,"0");return n}async function We(t){return Ms(await crypto.subtle.digest("SHA-256",t))}var Es=["name","type","tags","location","site","district","region","organization","path","timeZone"],Ss=["text","date","currency","truncate"],Et={site:"SITE",district:"DISTRICT",region:"REGION",organization:"ORGANIZATION"};function ke(t,e,n,s){if(!t)return n==="timeZone"&&e?e:void 0;if(n==="fields")return s&&t.fields?.[s]||void 0;let i=t.path??[];switch(n){case"name":return t.name||void 0;case"type":return t.type||void 0;case"tags":return t.tags?.length?t.tags.join(", "):void 0;case"location":return i.at(-1)?.name;case"path":return i.length?i.map(o=>o.name).join(" \u203A "):void 0;case"timeZone":return e||void 0;default:return Et[n]?[...i].reverse().find(o=>o.kind===Et[n])?.name:void 0}}function Ts(t,e){if(t==null||t==="")return;let n=String(t);if(e==="currency"){let s=Number(t);return Number.isFinite(s)?s.toFixed(2):n}if(e==="date"){let s=Date.parse(n);return Number.isNaN(s)?n:new Date(s).toISOString().slice(0,10)}return e==="truncate"&&n.length>40?n.slice(0,39)+"\u2026":n}function ze(t,e){return(t.source==="screen"?ke(e.screen,e.timeZone,t.field,t.key):Ts(e.tables?.[t.table]?.rows?.[t.row??0]?.[t.column],t.format))??t.fallback??""}function Ps(t){let e=/^\s*(?:screen\.(fields\.[a-z][A-Za-z0-9]{0,31}|[A-Za-z]+)|table\.([A-Za-z0-9_-]+)\[(\d+)\](?:\.([A-Za-z0-9_]+)|\["((?:[^"\\]|\\.)*)"\]))\s*((?:\|\s*(?:[a-z]+|"(?:[^"\\]|\\.)*")\s*)*)$/.exec(t);if(!e)return null;let n=[...e[6].matchAll(/\|\s*([a-z]+|"((?:[^"\\]|\\.)*)")/g)],s=n.find(o=>o[1].startsWith('"'))?.[2]?.replace(/\\(.)/g,"$1"),i=n.find(o=>!o[1].startsWith('"'))?.[1];return e[1]!==void 0?i?null:e[1].startsWith("fields.")?{source:"screen",field:"fields",key:e[1].slice(7),...s!==void 0?{fallback:s}:{}}:Es.includes(e[1])?{source:"screen",field:e[1],...s!==void 0?{fallback:s}:{}}:null:i&&!Ss.includes(i)?null:{source:"table",table:e[2],row:Number(e[3]),column:e[4]??e[5].replace(/\\(.)/g,"$1"),...i?{format:i}:{},...s!==void 0?{fallback:s}:{}}}function Ve(t,e){return t.replace(/\{{2}([^{}]*)\}{2}/g,(n,s)=>{let i=Ps(s);return i?ze(i,e):n})}function St(t,e){return Object.fromEntries(Object.entries(t).map(([n,s])=>[n,typeof s=="string"?Ve(s,e):s]))}var Ke=t=>t==null||t==="";function Pt(t,e,n,s,i,o){return Ls(t,e,n,s,{now:i,timeZone:o})}function Ls(t,e,n,s,i){if(n===null&&(e==="eq"||e==="ne"))return e==="eq"?Ke(t):!Ke(t);if(Ke(t)||n===null||n===void 0)return!1;switch(s){case"number":case"currency":{let o=Number(t),r=Number(n);return typeof n!="boolean"&&n!==""&&Number.isFinite(o)&&Number.isFinite(r)&&Ze(e,o-r)}case"boolean":{let o=n===!0||n==="true"?!0:n===!1||n==="false"?!1:void 0;return o!==void 0&&(e==="eq"?t===o:e==="ne"?t!==o:!1)}case"date":{let o=n==="today"?Is(i.now,i.timeZone).date:String(n),r=String(t).slice(0,10);return/^\d{4}-\d{2}-\d{2}$/.test(o)&&Ze(e,r<o?-1:r>o?1:0)}case"datetime":{let o=Date.parse(String(t)),r=n==="now"?i.now:Date.parse(String(n));return!Number.isNaN(o)&&!Number.isNaN(r)&&Ze(e,o-r)}default:{let o=String(t).toLowerCase(),r=String(n).toLowerCase();return e==="eq"?o===r:e==="ne"?o!==r:e==="contains"?o.includes(r):!1}}}function Ze(t,e){switch(t){case"eq":return e===0;case"ne":return e!==0;case"lt":return e<0;case"lte":return e<=0;case"gt":return e>0;case"gte":return e>=0;default:return!1}}var Tt=new Map;function Is(t,e){let n=Tt.get(e);n||(n=new Intl.DateTimeFormat("en-US",{timeZone:e,hourCycle:"h23",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",weekday:"short"}),Tt.set(e,n));let s=Object.fromEntries(n.formatToParts(new Date(t)).map(i=>[i.type,i.value]));return{date:`${s.year}-${s.month}-${s.day}`,minute:Number(s.hour)%24*60+Number(s.minute),day:String(s.weekday).slice(0,3).toLowerCase()}}var Lt=(t,e)=>!t||(t.from===void 0||t.from<=e)&&(t.until===void 0||e<t.until);var ie=class{constructor(e,n,s,i,o){h(this,"renderer",e);h(this,"wrap",i);h(this,"onShown",o);h(this,"timer");h(this,"turns",0);h(this,"turnAt");h(this,"left");h(this,"stopped",!1);h(this,"share");h(this,"pages");this.pages=n.pages??[],this.share=n.durationMs/Math.max(1,this.pages.length),this.turnAt=s+this.share,this.arm()}arm(){clearTimeout(this.timer);let e=this.pages.length;if(this.stopped||this.left!==void 0||e<2||!this.wrap&&this.turns+1>=e)return;let n=this.pages[(this.turns+1)%e];this.renderer.prepare?.(n).catch(()=>{}),this.timer=setTimeout(()=>{this.turn(n)},Math.min(Math.max(0,this.turnAt-Date.now()),2**31-1))}async turn(e){try{await this.renderer.show(e)}catch{}this.stopped||(this.turns++,this.turnAt+=this.share,this.onShown?.(e),this.arm())}pause(){this.stopped||this.left!==void 0||(clearTimeout(this.timer),this.left=Math.max(0,this.turnAt-Date.now()))}resume(){this.stopped||this.left===void 0||(this.turnAt=Date.now()+this.left,this.left=void 0,this.arm())}stop(){if(!this.stopped){this.stopped=!0,clearTimeout(this.timer);for(let e of this.pages)this.renderer.release?.(e)}}};var Je=class extends Error{constructor(n,s){super(s);h(this,"code",n);this.name="MediaError"}};function Ye(t){return t instanceof Je?{code:t.code.slice(0,64),message:t.message.slice(0,1e3)}:{code:"not-shown",message:String(t instanceof Error?t.message:t??"").slice(0,1e3)}}var It=t=>t.blob.contentType.startsWith("video/");var Xe="position:absolute;inset:0;width:100%;height:100%;object-fit:contain",Rs=1,xe=class{constructor(e,n={}){h(this,"container",e);h(this,"options",n);h(this,"prepared",new Map);h(this,"shown",null);h(this,"endListeners",new Set);h(this,"itemListeners",new Set);h(this,"appListeners",new Set);h(this,"held",null);h(this,"drawn",0);h(this,"shownHash",null);let s=n.media;s&&(document.documentElement.style.background="transparent",document.body.style.background="transparent",e.style.background="#000",s.onEnd((i,o)=>{let r=this.shown;if(r?.mediaId===i)for(let a of this.endListeners)a(r.slide,o)}))}onMediaEnd(e){this.endListeners.add(e)}onElementItem(e){this.itemListeners.add(e)}onApp(e){this.appListeners.add(e)}app(e){for(let n of this.appListeners)n(e);this.options.onApp?.(e)}prepare(e){let n=this.prepared.get(e);if(n)return n.ready;let s;try{s=this.make(e)}catch(i){return Promise.reject(i)}return this.prepared.set(e,s),s.ready.catch(()=>{this.prepared.get(e)===s&&(this.prepared.delete(e),this.dispose(s))}),s.ready}release(e){let n=this.prepared.get(e);n&&(this.prepared.delete(e),this.dispose(n))}async show(e){await this.prepare(e);let n=this.prepared.get(e);if(!n)throw new Error("the slide was released before it was shown");this.prepared.delete(e);let s=this.shown,i=this.options.media,o=s?Rt(e):void 0;if(n.kind==="layout"&&o===void 0){await H(()=>{s?this.container.insertBefore(n.el,s.el):this.container.append(n.el),this.shown=n,this.shownHash=e.blob.hash}),this.held=[],await n.view.start().catch(()=>{});let r=Promise.resolve();return await H(()=>{this.shown===n&&(n.el.style.visibility="",s?.el.remove(),i&&this.paint("transparent"),i&&s?.mediaId&&(r=i.show([],[s.mediaId],Date.now())))}),await r.catch(()=>{}),s&&this.dispose(s),this.releaseHeld(),"cut"}if(o!==void 0&&i&&n.kind==="native"&&s.kind==="native"){if(n.fadesIn&&!n.queued){let r=Promise.resolve();return await H(()=>{n.el.style.visibility="",this.container.insertBefore(n.el,s.el),r=i.show([n.mediaId],[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{}),Ns(o).then(async()=>{await i.show([],[s.mediaId],Date.now()).catch(()=>{}),s.el.remove(),this.dispose(s)}),"crossfade"}}else if(o!==void 0&&i&&n.kind==="native"){let r=Promise.resolve();return await H(()=>{n.el.style.visibility="",this.container.insertBefore(n.el,s.el),r=i.show([n.mediaId],[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{}),this.paint("transparent"),Nt(s.el,1,0,o).then(()=>{s.el.remove(),this.dispose(s)}),"crossfade"}else if(o!==void 0)return await H(()=>{n.el.style.visibility="",n.el.style.opacity="0",this.container.append(n.el),n.kind==="video"&&Ge(n.el),this.shown=n,this.shownHash=e.blob.hash}),n.view&&(this.held=[],n.view.start().then(()=>this.releaseHeld())),Nt(n.el,0,1,o).then(async()=>{s.el.remove(),this.shown===n&&this.paint(i?"#000":""),i&&s.mediaId&&await i.show([],[s.mediaId],Date.now()).catch(()=>{}),this.dispose(s)}),"crossfade";if(i&&n.kind==="native"){let r=Promise.resolve();await H(()=>{n.el.style.visibility="",s?this.container.insertBefore(n.el,s.el):this.container.prepend(n.el),r=i.show([n.mediaId],s?.mediaId?[s.mediaId]:[],Date.now()),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{}),await H(()=>{this.shown===n&&(s?.el.remove(),this.paint("transparent"))})}else{let r=Promise.resolve();await H(()=>{n.el.style.visibility="",n.el.parentNode!==this.container&&this.container.append(n.el),s?.el.remove(),this.paint(i?"#000":""),i&&s?.mediaId&&(r=i.show([],[s.mediaId],Date.now())),n.kind==="video"&&Ge(n.el),this.shown=n,this.shownHash=e.blob.hash}),await r.catch(()=>{})}return s&&this.dispose(s),"cut"}paint(e){this.options.background?this.options.background(e):this.container.style.background=e}standby(){let e=this.shown;this.shown=null,this.shownHash=null,this.paint(this.options.media?"#000":""),e&&(e.mediaId&&this.options.media?.show([],[e.mediaId],Date.now()).catch(()=>{}),this.dispose(e))}pause(){let e=this.shown;e?.view?.pause(),e?.kind==="video"?e.el.pause():e?.mediaId&&this.options.media?.control(e.mediaId,"pause").catch(()=>{})}resume(){let e=this.shown;e?.view?.resume(),e?.kind==="video"?Ge(e.el):e?.mediaId&&this.options.media?.control(e.mediaId,"play").catch(()=>{})}make(e){if(e.layout){let{doc:r,source:a,untilPass:l}=e.layout,d=K(D("div"));d.dataset.blob=e.blob.hash,d.style.cssText=`${Xe};visibility:hidden`,this.container.append(d);let u={slide:e,kind:"layout",el:d,ready:Promise.resolve()},p=y=>this.options.path?`${this.options.path}/${y}`:y,m=String(++this.drawn);return u.view=new J(r,a,d,{media:this.options.media,path:this.options.path,endsWith:l?p(l):void 0,onItem:y=>{let A={...y,slide:y.slide?`${m}.${y.slide}`:m};this.held?this.held.push(A):this.item(A)},onPassEnd:y=>{if(l&&y===p(l)&&this.shown===u){for(let A of this.endListeners)A(e);return"hold"}return this.options.onPassEnd?.(y)},onApp:y=>this.app(y)}),u.ready=u.view.ready,u}if(!It(e)){if(!e.bytes&&!e.url)throw new Error(`no bytes for ${e.blob.hash}`);let r=e.bytes?URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})):void 0,a=K(D("img"));return a.src=r??e.url,a.alt="",a.dataset.blob=e.blob.hash,a.style.cssText=this.fill(),{slide:e,kind:"image",el:a,objectUrl:r,ready:a.decode()}}let n=this.options.media;if(n){let r=`m-${Rs++}`,a=K(D("div"));a.dataset.blob=e.blob.hash,a.dataset.media=r,a.style.cssText=`${Xe};visibility:hidden`;let l=this.container.getBoundingClientRect(),d={x:l.left,y:l.top,w:l.width||window.innerWidth,h:l.height||window.innerHeight},u=Rt(e),p=u!==void 0&&this.shown?.kind==="native"&&n.capability.animates&&n.capability.layers>=2,m=p?[{property:"opacity",keyframes:[{t:0,v:0},{t:u,v:1}]}]:[],y={slide:e,kind:"native",el:a,mediaId:r,fadesIn:p,ready:Promise.resolve()},A=this.options.fit??"contain";return y.ready=n.prepare({mediaId:r,blob:e.blob.hash,contentType:e.blob.contentType,box:d,z:p?1:0,loop:e.loop,muted:!1,fit:A,...p?{tracks:m}:{}}).then(f=>{f.queued&&(y.queued=!0)}),y}let s=e.url?void 0:URL.createObjectURL(new Blob([e.bytes],{type:e.blob.contentType})),i=K(D("video"));i.dataset.blob=e.blob.hash,i.style.cssText=`${this.fill()};visibility:hidden`,i.muted=!1,i.playsInline=!0,i.preload="auto",i.loop=e.loop,i.src=e.url??s,i.addEventListener("ended",()=>{if(this.shown?.el===i)for(let r of this.endListeners)r(e)}),i.addEventListener("error",()=>{if(this.shown?.el===i)for(let r of this.endListeners)r(e,`video: ${i.error?.message??"error"}`)});let o=new Promise((r,a)=>{if(i.readyState>=2)return r();i.addEventListener("loadeddata",()=>r(),{once:!0}),i.addEventListener("error",()=>a(new Error(`video: ${i.error?.message??"cannot load"}`)),{once:!0})});return this.container.append(i),{slide:e,kind:"video",el:i,objectUrl:s,ready:o}}item(e){this.options.onItem?.(e);for(let n of this.itemListeners)n(e)}releaseHeld(){let e=this.held;this.held=null,e?.length&&setTimeout(()=>{for(let n of e)this.item(n)},0)}fill(){let e=this.options.fit==="stretch"?"fill":this.options.fit??"contain";return Xe.replace("object-fit:contain",`object-fit:${e}`)}dispose(e){if(this.shown!==e){if(e.view?.dispose(),e.el.remove(),e.kind==="video"){let n=e.el;n.pause(),n.removeAttribute("src"),n.load()}e.mediaId&&this.options.media?.release(e.mediaId).catch(()=>{}),e.objectUrl&&URL.revokeObjectURL(e.objectUrl)}}};function Rt(t){if(t.transition?.type==="crossfade")return Math.max(0,Math.min(t.transition.durationMs??1e3,t.durationMs/2))}var Ns=t=>new Promise(e=>setTimeout(e,t));function Nt(t,e,n,s){return new Promise(i=>{let o=Date.now(),r=!1,a=()=>{r||(r=!0,t.style.opacity=n===1?"":String(n),i())};t.style.opacity=String(e);let l=()=>{if(r)return;let d=Math.min(1,(Date.now()-o)/s);if(d>=1)return a();t.style.opacity=String(e+(n-e)*d),requestAnimationFrame(l)};requestAnimationFrame(l),setTimeout(a,s+250)})}function Ge(t){t.play().catch(()=>(t.muted=!0,t.play())).catch(()=>{})}function H(t){return new Promise((e,n)=>{let s=!1,i=()=>{if(!s){s=!0;try{t(),e()}catch(o){n(o)}}};requestAnimationFrame(i),setTimeout(i,250)})}var re={apiVersion:2,properties:[{name:"device",description:"What the device is: id, type, shell and versions, model, resolution and orientation. Fixed while the player runs."},{name:"mode",description:"Where player-core runs: player, preview (in the CMS) or designer. In preview and the designer, commands change nothing."},{name:"timeZone",description:"The player's time zone (IANA), from the host.",event:"time.changed"},{name:"locale",description:"The player's locale (BCP 47), from the host."},{name:"connection",description:"Whether the player is connected to Wyndlass.",event:"connection.changed"},{name:"state",description:"starting, playing, paused, stopped (the standby screen), idle (nothing to play) or error.",event:"state.changed"},{name:"held",description:"A remote pause or stop in effect, by whom and since when; absent when none.",event:"state.changed"},{name:"content",description:"The manifest playing, when it was issued and switched to; absent when there is none.",event:"content.updated"},{name:"now",description:"What each zone shows: the item, its blob and content type, when it started, and its duration and position when known.",event:"item.start"},{name:"audio",description:"The player's volume (0 to 100) and whether it is muted; only when the host can set them.",event:"audio.changed"},{name:"display",description:"The display's power and brightness; only when the host can report them.",event:"display.changed"},{name:"capabilities",description:"What the host can do: audio, display power, display brightness."}],events:[{name:"state.changed",description:"The player's state changed (playing, paused, stopped, idle, error), and who changed it."},{name:"content.updated",description:"A new manifest was switched to."},{name:"item.start",description:"An item started in a zone (source start)."},{name:"item.end",description:"An item ended in a zone (source end): completed, skipped, stopped, error, replaced by new content, or finished by its app."},{name:"item.error",description:"An item could not be shown."},{name:"connection.changed",description:"The player connected to Wyndlass or lost its connection."},{name:"audio.changed",description:"The volume or mute changed, and who changed it."},{name:"display.changed",description:"The display's power or brightness changed, and who changed it."},{name:"time.changed",description:"The clock was adjusted or the time zone changed."},{name:"app.command",description:"A command for apps on the screen (apps.sendCommand); an app reacts if it knows the name."},{name:"app.failed",description:"An app on the screen could not run, or misbehaved (WP-0100): its bundle did not load, it was not an app, it needs a newer player, it kept throwing and was stopped, or it was late or overran a clip."},{name:"app.held",description:"An app version was held back on this screen until the next manifest, after the page stopped answering twice in an hour while it was on screen (WP-0100)."}],commands:[{name:"player.status",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/status",description:"Everything about the player now: device, mode, time zone, locale, connection, state (and any hold), the content playing, what each zone shows, audio, display and what the host can do. Changes nothing.",idempotent:!0},{name:"player.events",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/events",result:"player-api-results.schema.json#/$defs/events",description:"The player's recent events (the last 200), oldest first, after a sequence number: state changes, content updates, item starts and ends, errors, connection, audio, display and time. Pass next as after to poll for more. Changes nothing.",idempotent:!0},{name:"time.now",access:"read",apps:"read",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/timeNow",description:"The player's current time, its time zone and UTC offset, and whether its clock is synchronized. Changes nothing.",idempotent:!0},{name:"player.play",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Resumes or starts playback on the screen. From a remote caller it also ends a remote pause or stop.",idempotent:!0},{name:"player.pause",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Freezes the screen on what it shows now: the loop's timer and any media stop where they are. From a remote caller it holds until a remote player.play or a restart; apps cannot resume it, and new content arrives paused.",idempotent:!0},{name:"player.stop",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/state",description:"Stops playback and shows the standby screen (black, display on). From a remote caller it holds until a remote player.play or a restart; apps cannot restart it, and new content arrives stopped.",idempotent:!0},{name:"player.next",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/zone",result:"player-api-results.schema.json#/$defs/now",description:"Skips to the next item in a zone (main by default); the current item ends as skipped."},{name:"player.previous",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/zone",result:"player-api-results.schema.json#/$defs/now",description:"Goes back to the previous item in a zone (main by default); the current item ends as skipped."},{name:"player.playItem",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/playItem",result:"player-api-results.schema.json#/$defs/now",description:"Shows a given item of the content playing now in a zone (main by default), then continues the loop from it. Refused for an item the playing manifest does not have."},{name:"audio.setVolume",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setVolume",result:"player-api-results.schema.json#/$defs/audio",description:"Sets the player's volume, 0 to 100. Only on players whose host can set it to a level.",needs:"audio.volume",idempotent:!0},{name:"audio.setMuted",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setMuted",result:"player-api-results.schema.json#/$defs/audio",description:"Mutes or unmutes the player. Only on players whose host can.",needs:"audio",idempotent:!0},{name:"display.setPower",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setPower",result:"player-api-results.schema.json#/$defs/display",description:"Turns the display on or to standby. Only on players whose host can. Standby blanks the screen until it is turned on.",needs:"display.power",idempotent:!0},{name:"display.setBrightness",access:"control",apps:"control",remote:!0,params:"player-api-params.schema.json#/$defs/setBrightness",result:"player-api-results.schema.json#/$defs/display",description:"Sets the display's brightness, 0 to 100. Only on players whose host can.",needs:"display.brightness",idempotent:!0},{name:"apps.sendCommand",access:"control",apps:"none",remote:!0,params:"player-api-params.schema.json#/$defs/sendCommand",result:"player-api-results.schema.json#/$defs/delivered",description:"Sends a named command, with an optional argument, to the apps on the screen; an app that knows the name reacts. Returns how many apps listen."},{name:"app.finish",access:"control",apps:"read",remote:!1,params:"player-api-params.schema.json#/$defs/empty",result:"player-api-results.schema.json#/$defs/none",description:"An app ends its own slot now: its item ends as finished and the loop moves on. Apps only; not a tool."}],host:[{name:"host.describe",kind:"request",description:"What the device is, its time zone, locale, connection and capabilities.",result:"player-api-host.schema.json#/$defs/describeResult"},{name:"host.time",kind:"request",description:"The host's time and time zone.",result:"player-api-common.schema.json#/$defs/timeNow"},{name:"host.getAudio",kind:"request",description:"The player's volume and mute.",result:"player-api-common.schema.json#/$defs/audio"},{name:"host.setAudio",kind:"request",description:"Sets the volume, the mute or both; answers the audio after.",result:"player-api-common.schema.json#/$defs/audio"},{name:"host.getDisplay",kind:"request",description:"The display's power and brightness.",result:"player-api-common.schema.json#/$defs/display"},{name:"host.setDisplay",kind:"request",description:"Sets the display's power, brightness or both; answers the display after.",result:"player-api-common.schema.json#/$defs/display"},{name:"host.adRequest",kind:"request",description:"Asks Wyndlass's ad broker for one slot of a break over the host's NATS connection (WP-0075); the result is the broker's ad-decision (ad-decision.schema.json). Only a host whose describe says ads is asked."},{name:"host.mediaPrepare",kind:"request",description:"Lays a video out behind the page and prepares it to its first frame; host.mediaState says when (ADR 0055)."},{name:"host.mediaShow",kind:"request",description:"Shows and hides prepared videos; replies once its layer has changed."},{name:"host.mediaControl",kind:"request",description:"Plays, pauses or seeks a video, or asks for its position."},{name:"host.mediaRelease",kind:"request",description:"Removes a video and frees its decoder."},{name:"host.reply",kind:"reply",description:"The host's answer to a request: result or error (unsupported, failed)."},{name:"host.connection",kind:"event",description:"The connection to Wyndlass changed."},{name:"host.timeChanged",kind:"event",description:"The clock was adjusted or the time zone changed."},{name:"host.audioChanged",kind:"event",description:"The volume or mute changed outside the player."},{name:"host.displayChanged",kind:"event",description:"The display changed outside the player."},{name:"host.mediaState",kind:"event",description:"A video was prepared, started, ended or failed, or its position was asked for."},{name:"host.presence",kind:"event",description:"What one of the host's sensors sees now: the presences in view, with random ids that never leave the page (WP-0105)."},{name:"host.sensing",kind:"event",description:"The site's sensing mode as the host applies it, and its sensors (WP-0105)."},{name:"host.serialPorts",kind:"event",description:"The host's sensor controller ports, and which are open (WP-0043)."},{name:"host.serialLine",kind:"event",description:"One line from a sensor controller, as it arrived (WP-0043)."},{name:"host.serialWrite",kind:"request",description:"Writes lines to a sensor controller, a gap apart; replies once written (WP-0043)."},{name:"host.bleWatch",kind:"request",description:"What the page listens for: beacons' iBeacon UUIDs, Eddystone namespaces and addresses; replies once the scan follows it. Sent only to a host that has sent host.bleState (WP-0044)."},{name:"host.bleAdverts",kind:"event",description:"Beacon frames that match the watch, at most four a second of each kind from each beacon (WP-0044)."},{name:"host.bleState",kind:"event",description:"The host's Bluetooth scanner: scanning, idle, off, refused, missing or failed; sent only by a host that has one (WP-0044)."},{name:"host.gpioPins",kind:"event",description:"The GPIO pins the host reads, with their values (WP-0044)."},{name:"host.gpioEdge",kind:"event",description:"A GPIO pin changed (WP-0044)."},{name:"host.mcp",kind:"relay",description:"An MCP message from ctl.dev.{deviceId}, for player-core to answer."},{name:"core.ready",kind:"core",description:"player-core is ready to answer host.mcp; until then the shell answers -32002 itself."},{name:"core.mcpReply",kind:"core",description:"player-core's answer to a host.mcp, for the shell to send back."},{name:"core.sensors",kind:"core",description:"The page's sensor controllers, channels and inputs, and the beacons it hears, whenever they change, for the heartbeat and sensors.recent (WP-0043, WP-0044)."}]};var Ot=Symbol.for("wyndlass.app.attach");var F=class extends Error{constructor(n,s){super(`the app could not run (${n})`);h(this,"reason",n);h(this,"error",s)}};function W(t){try{let e=t instanceof Error?t.name:void 0;return typeof e=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(e)?e:void 0}catch{return}}function oe(t){let e=t?.id;if(typeof e!="string"||e==="")return!0;for(let n=0;n<$t.length;n++)if(e[n]!==$t[n])return!1;return!0}var $t="com.wyndlass.";function $s(t=typeof navigator>"u"?"":navigator.userAgent){let e=/Chrom(?:e|ium)\/(\d+)/.exec(t);return e?Number(e[1]):void 0}function Dt(t,e=re.apiVersion,n=$s()){return!(t?.playerApi!==void 0&&t.playerApi>e||t?.webView!==void 0&&n!==void 0&&t.webView>n)}function Ds(t){let e;try{e=t.paging?.()}catch{return null}let n=e;return n&&Number.isInteger(n.count)&&n.count>1&&typeof n.ms=="number"&&n.ms>0?{count:n.count,ms:n.ms}:null}var Qe=new Map;function js(){let t=kt(8),e="";for(let n=0;n<8;n++)e+=Fs[t[n]%26];return e}var Fs="abcdefghijklmnopqrstuvwxyz",Hs=0;function jt(t,e,n){let s=Qe.get(t);if(!s){let i=je.make([e],"text/javascript");s=import(i).then(o=>Bs(o,n),o=>{throw o instanceof F?o:new F("load",W(o))}).finally(()=>je.revoke(i)),Qe.set(t,s),s.catch(()=>Qe.delete(t))}return s}function Bs(t,e){let n=t.default;if(typeof n=="function"&&n.prototype instanceof HTMLElement){let s=n.prototype[Ot];if(typeof s!="function")throw new F("shape");let i=class extends n{},o=`${e?.element??"wl-app"}-${js()}${++Hs}`;try{He.define(o,i)}catch(r){throw new F("shape",W(r))}return{tag:o,shape:"class",attach:(r,a)=>{vt(s,r,[a])}}}if(oe(e)&&typeof t.element=="string"&&He.get(t.element))return{tag:t.element,shape:"legacy"};throw new F("shape")}var Us=3,qs=6e4,Ae=class{constructor(e,n){h(this,"element",e);h(this,"o",n);h(this,"faults",[]);h(this,"halted",!1)}get stopped(){return this.halted}has(e){try{return!this.halted&&typeof this.element[e]=="function"}catch{return!1}}update(e){this.halted||this.run(()=>this.element.update?.(e))}reset(){this.halted||this.run(()=>this.element.reset?.())}paging(){return this.halted?null:Ds(this.element)}hook(e,n){if(this.halted)return Promise.resolve();let s;try{s=this.element[e]?.(n)}catch(i){return this.fault(i),Promise.resolve()}return Promise.resolve(s).then(()=>{},i=>this.fault(i))}stop(e){if(!this.halted){this.halted=!0;try{this.element.remove()}catch{}this.o.dispose?.(),this.o.onStop(e)}}dispose(){this.o.dispose?.()}run(e){try{e()}catch(n){this.fault(n)}}fault(e){let n=(this.o.now??Date.now)();this.faults=this.faults.filter(s=>n-s<qs),this.faults.push(n),this.faults.length>=Us&&this.stop(W(e))}},_s=24*3600*1e3,Me=class t{constructor(e){h(this,"o",e);h(this,"memory",new Map);h(this,"inflight",new Map);h(this,"images",new Map);h(this,"pictures",new Map)}now(){return this.o.now?.()??Date.now()}url(e){let n=this.o.base();if(!n)return;let s=this.o.token(),i=`${n.replace(/\/$/,"")}${e}`;return s?`${i}${i.includes("?")?"&":"?"}${s}`:i}stored(e){return this.memory.get(e)??this.o.store?.getItem(e)??void 0}static keyOf(e,n){let s=Object.keys(n).sort().map(i=>`${encodeURIComponent(i)}=${encodeURIComponent(String(n[i]))}`).join("&");return{query:s,key:`wyndlass:data:${e}?${s}`}}kept(e,n){let s=this.stored(t.keyOf(e,n).key);if(s)try{let i=JSON.parse(s);return this.dress(i,Date.parse(i.expiresAt)<=this.now())}catch{return}}keep(e,n){this.memory.set(e,n);try{this.o.store?.setItem(e,n)}catch{}}get(e,n){let{query:s,key:i}=t.keyOf(e,n),o=this.inflight.get(i);if(o)return o;let r=this.o.fetch??fetch,a=(async()=>{let l=this.stored(i),d=l?JSON.parse(l):void 0;if(d&&Date.parse(d.expiresAt)>this.now())return this.dress(d,!1);let u=this.url(`/v0/${encodeURIComponent(e)}?${s}`);try{if(!u)throw new Error("no data service");let p=await r(u);if(!p.ok)throw new Error(`the data service answered ${p.status}`);let m=await p.text();return this.keep(i,m),this.dress(JSON.parse(m),!1)}catch(p){if(d&&Date.parse(d.expiresAt)+_s>this.now())return this.dress(d,!0);throw p}})().finally(()=>this.inflight.delete(i));return this.inflight.set(i,a),a}dress(e,n){return{...e,offline:n,ageMs:Math.max(0,this.now()-Date.parse(e.fetchedAt))}}image(e){let n=this.images.get(e);return n||(n=this.imageBlob(e).then(s=>URL.createObjectURL(s)),this.images.set(e,n),n.catch(()=>this.images.delete(e))),n}imageBlob(e){let n=this.pictures.get(e);if(!n){let s=this.url(e);n=s?(this.o.fetch??fetch)(s).then(i=>{if(!i.ok)throw new Error(`the image answered ${i.status}`);return i.blob()}):Promise.reject(new Error("no data service")),this.pictures.set(e,n),n.catch(()=>this.pictures.delete(e))}return n}};var Ft="wyndlass.frame";function Ut(t,e){return!!t&&(t.apps?t.apps(e):!oe(e))}var Ws=new Set(["load","shape","unsupported","error","late","overran"]),Ht=50,Bt=250,et=class{constructor(e,n,s,i){h(this,"frame",e);h(this,"port",n);h(this,"o",i);h(this,"halted",!1);h(this,"pages");h(this,"hooks");h(this,"pending",new Map);h(this,"next",0);h(this,"told");h(this,"last");h(this,"quiet");this.hooks=new Set(s.hooks),this.pages=s.paging}get stopped(){return this.halted}has(e){return!this.halted&&this.hooks.has(e)}paging(){return this.halted?null:this.pages}update(e){if(this.halted)return;let n=performance.now(),s=this.last;this.last={t:e,at:n};let i=this.told;if(i&&(i.running?Math.abs(e-(i.t+(n-i.at)))<=Ht:e===i.t)){i.running&&this.watch();return}let o=!!s&&n-s.at<Bt&&e>s.t&&Math.abs(e-s.t-(n-s.at))<=Ht;this.told={t:e,at:n,running:o},this.post({type:"clock",t:e,running:o}),o&&this.watch()}reset(){this.halted||this.post({type:"reset"})}hook(e,n){if(this.halted)return Promise.resolve();let s=++this.next;return new Promise(i=>{this.pending.set(s,i),this.post({type:"hook",id:s,kind:e,options:n})})}stop(e){this.halted||(this.halted=!0,this.frame.remove(),this.dispose(),this.o.onStop(e))}dispose(){clearTimeout(this.quiet),this.port.close();for(let e of this.pending.values())e();this.pending.clear()}heard(e){e.type==="paging"?this.pages=_t(e.paging):e.type==="hooked"&&typeof e.id=="number"?(this.pending.get(e.id)?.(),this.pending.delete(e.id)):e.type==="stopped"&&this.stop(Wt(e.error))}post(e){try{this.port.postMessage(e)}catch{}}watch(){clearTimeout(this.quiet),this.quiet=setTimeout(()=>{!this.told?.running||!this.last||this.halted||(this.told={...this.last,running:!1},this.post({type:"clock",t:this.last.t,running:!1}))},Bt)}};async function qt(t,e,n,s,i,o,r,a,l,d){let u=t.appInfo??n.info,p;try{p=new URL(l.frames).origin}catch{return d("unsupported")}let m=document.createElement("iframe");m.setAttribute("sandbox","allow-scripts"),m.setAttribute("referrerpolicy","no-referrer"),m.title=u?.id??"app",m.tabIndex=-1,m.dataset.frame=u?.id??"",m.style.cssText="display:block;width:100%;height:100%;border:0;pointer-events:none;background:transparent;color-scheme:normal";let y=new Promise(M=>{m.addEventListener("load",()=>M(!0),{once:!0}),setTimeout(()=>M(!1),o)});m.src=`${p}/`,e.append(m);let A=(M,w)=>(m.remove(),d(M,w));if(!await y||!m.contentWindow)return A("load");let{port1:f,port2:k}=new MessageChannel,b={type:"init",element:{id:t.id,type:"app",app:t.app,props:t.props??{},...t.binding?{binding:t.binding}:{},...t.appInfo?{appInfo:t.appInfo}:{}},bundle:{hash:n.hash,bytes:n.bytes.slice(),...n.info?{info:n.info}:{}},...s.place?{place:s.place}:{},screen:s.screen??null,timeZone:s.timeZone,locale:s.locale,preview:!!s.preview,clock:{t:i.now(),running:!1,startedAt:i.startedAt()},fonts:Ks(t.props??{},l),snapshots:Zs(t,s),deadlineMs:o};return new Promise(M=>{let w,T=!1,N=G=>{T||(T=!0,clearTimeout(j),M(G))},j=setTimeout(()=>{w?(N({app:w,late:!0}),w.update(i.now())):(f.close(),N(A("load")))},o+1e3),q=!1;f.onmessage=G=>{let x=G.data;if(!(!x||typeof x!="object")){if(x.type==="ask"){q&&zs(x,s,f);return}if(w){x.type==="ready"?(N({app:w,late:x.late===!0}),w.update(i.now())):w.heard(x);return}x.type==="hello"&&!q?(q=!0,f.postMessage(b,[b.bundle.bytes.buffer])):x.type==="made"&&Array.isArray(x.hooks)?(w=new et(m,f,{type:"made",hooks:x.hooks.filter(C=>C==="enter"||C==="exit"),paging:_t(x.paging)},{onStop:a}),r(w)):x.type==="failed"&&(f.close(),N(A(Ws.has(x.reason)?x.reason:"error",Wt(x.error))))}},m.contentWindow.postMessage({type:Ft},"*",[k])})}async function zs(t,e,n){if(typeof t.id!="number")return;let s=(i,o=[])=>{try{n.postMessage(i,o)}catch{}};try{if(!e.data)throw new Error("no data service");if(t.what==="data"&&typeof t.source=="string")s({type:"answer",id:t.id,value:await e.data.get(t.source,Vs(t.params))});else if(t.what==="image"&&typeof t.path=="string"){let i=await e.data.imageBlob(t.path),o=new Uint8Array(await i.arrayBuffer());s({type:"answer",id:t.id,value:{bytes:o,type:i.type}},[o.buffer])}else throw new TypeError("not a question")}catch(i){s({type:"answer",id:t.id,error:W(i)??"Error"})}}function Vs(t){let e={};if(t&&typeof t=="object")for(let[n,s]of Object.entries(t))(typeof s=="string"||typeof s=="number"||typeof s=="boolean")&&(e[n]=s);return e}function _t(t){let e=t;return e&&Number.isInteger(e.count)&&e.count>1&&typeof e.ms=="number"&&e.ms>0?{count:e.count,ms:e.ms}:null}function Wt(t){return typeof t=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(t)?t:void 0}function Ks(t,e){let n=new Set;for(let i of Object.values(t)){let o=i?.family;i&&typeof i=="object"&&typeof o=="string"&&n.add(o)}let s=[];for(let i of e.fonts??[]){let o=n.has(i.family)?e.font?.(i.blob):void 0;o&&s.push({family:i.family,weight:i.weight,style:i.style,blob:i.blob,bytes:o.slice()})}return s}function Zs(t,e){let n=new Set;t.binding?.table&&n.add(t.binding.table);for(let i of Object.values(t.props??{}))typeof i=="string"&&/^tbl_[0-9a-z]{26}$/.test(i)&&n.add(i);let s={};for(let i of n){let o=e.snapshot?.(i);o&&(s[i]=o)}return s}function zt(t){let e=te?.setStyle??((n,s,i,o)=>n.style.setProperty(s,i,o));e(t,"contain","strict","important"),e(t,"overflow","clip","important")}var Vt=t=>!!(t?.filter?.length||t?.sort||t?.limit),Js=t=>t==null||t==="";function Ys(t,e){if(!Js(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 Xs(t,e){let n=Array.from(t),s=Array.from(e);for(let i=0;i<n.length&&i<s.length;i++){let o=n[i].codePointAt(0)-s[i].codePointAt(0);if(o!==0)return o}return n.length-s.length}function Gs(t,e,n,s,i){let o=t,r=Array.isArray(o?.rows)?o.rows:[],a=Array.isArray(o?.columns)?o.columns:void 0,l=d=>a?.find(u=>u.key===d)?.type??"text";for(let d of e.filter??[]){if(a&&!a.some(m=>m.key===d.column))return[];let u=d.value;if(u!==null&&typeof u=="object"){let m=u;if(u=ke(n,s,m.screen,m.key),u===void 0)return[]}let p=l(d.column);r=r.filter(m=>Pt(m[d.column],d.op,u,p,i,s))}if(e.sort){let{column:d}=e.sort,u=l(d),p=e.sort.order==="desc"?-1:1;r=r.map((m,y)=>({row:m,i:y,key:Ys(m[d],u)})).sort((m,y)=>{if(m.key===void 0||y.key===void 0)return m.key===void 0&&y.key===void 0?m.i-y.i:m.key===void 0?1:-1;let A=typeof m.key=="number"&&typeof y.key=="number"?m.key-y.key:Xs(String(m.key),String(y.key));return A===0?m.i-y.i:A<0?-p:p}).map(m=>m.row)}return e.limit?r.slice(0,e.limit):r}function Kt(t,e,n,s,i){return!t||typeof t!="object"?t:{...t,rows:Gs(t,e,n,s,i)}}function Qs(t){if(!t)return"#000000";if("value"in t)return t.value;let e=t.gradient,n=[...e.stops].sort((s,i)=>s.at-i.at).map(s=>`${s.color.value} ${Math.round(s.at*1e4)/100}%`).join(", ");return e.kind==="radial"?`radial-gradient(circle, ${n})`:`linear-gradient(${e.angle??180}deg, ${n})`}function Jt(t,e,n,s=[]){let i=D("div");if(i.dataset.background="",i.style.cssText="position:absolute;inset:0;pointer-events:none",i.style.background=Qs(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(""),r=`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${n.width} ${n.height}" preserveAspectRatio="none"><path fill-rule="evenodd" d="${o}"/></svg>`,a=`url("data:image/svg+xml,${encodeURIComponent(r)}")`;i.dataset.holes=String(s.length);for(let[l,d]of[["maskImage",a],["maskSize","100% 100%"],["maskRepeat","no-repeat"]])i.style[l]=d,i.style[`webkit${l[0].toUpperCase()}${l.slice(1)}`]=d}return t.prepend(i),i}function Yt(t,e=t.frame){let n=D("div");return n.dataset.element=t.id,n.style.cssText="position:absolute;box-sizing:border-box",ei(n,e),n}function ei(t,e){Object.assign(t.style,{left:`${e.x}px`,top:`${e.y}px`,width:`${e.w}px`,height:`${e.h}px`,transform:e.rotation?`rotate(${e.rotation}deg)`:""})}function Xt(t,e,n){if(e.type==="text"){let s=e.style??{},i=Gt(n);t.textContent=e.bind?ze(e.bind,i):Ve(e.text??"",i),Object.assign(t.style,{fontFamily:s.font?.family?`"${s.font.family}", sans-serif`:"",fontWeight:String(s.font?.weight??400),fontStyle:s.font?.style??"normal",fontSize:`${s.size??48}px`,color:s.color?.value??"#ffffff",background:s.background?.value??"",whiteSpace:"pre-wrap",textAlign:{start:"left",end:"right"}[s.align??"start"]??s.align??"left",display:"flex",flexDirection:"column",justifyContent:{top:"flex-start",middle:"center",bottom:"flex-end"}[s.verticalAlign??"top"],lineHeight:s.lineHeight?String(s.lineHeight):"",opacity:s.opacity!==void 0?String(s.opacity):"",textDecorationLine:s.decoration?.length?s.decoration.join(" "):"",textTransform:s.transform&&s.transform!=="none"?s.transform:"",letterSpacing:s.letterSpacing?`${s.letterSpacing}em`:"",textShadow:s.shadow?`${s.shadow.x??0}px ${s.shadow.y??2}px ${s.shadow.blur??4}px ${s.shadow.color?.value??"#000000"}`:""})}else e.type==="shape"&&ti(e)?(t.style.opacity=e.opacity!==void 0?String(e.opacity):"",t.append(ni(e))):e.type==="shape"?Object.assign(t.style,{background:e.fill?.value??"",opacity:e.opacity!==void 0?String(e.opacity):"",border:e.stroke?.width?`${e.stroke.width}px ${e.stroke.dash??"solid"} ${e.stroke.color?.value??"#000"}`:"",borderRadius:e.shape==="ellipse"?"50%":e.radius?`${e.radius}px`:"",overflow:e.clip!==!1?"hidden":""}):e.type==="library"&&(t.style.overflow="hidden",t.style.background=e.playback?.background?.value??"")}function Gt(t){return{screen:t?.screen??null,timeZone:t?.timeZone,tables:new Proxy({},{get:(e,n)=>typeof n=="string"?t?.snapshot?.(n):void 0})}}var ti=t=>t.shape==="line"||t.shape==="arrow"||t.shape==="polygon";function ni(t){let e=ye("svg");e.setAttribute("width","100%"),e.setAttribute("height","100%"),e.style.cssText="position:absolute;inset:0;overflow:visible";let n=t.frame.w,s=t.frame.h,i=t.stroke?.width??(t.shape==="polygon"?0:4),r=(t.points?.length?t.points:t.shape==="polygon"?[[.5,0],[1,1],[0,1]]:[[0,.5],[1,.5]]).map(([u,p])=>[u*n,p*s]),a=t.stroke?.color?.value??"#000000",l=t.stroke?.dash==="dashed"?`${i*3} ${i*2}`:t.stroke?.dash==="dotted"?`0 ${i*2}`:"",d=ye(t.shape==="polygon"?"polygon":"polyline");if(d.setAttribute("points",r.map(u=>`${u[0]},${u[1]}`).join(" ")),d.setAttribute("fill",t.shape==="polygon"?t.fill?.value??"none":"none"),i&&(d.setAttribute("stroke",a),d.setAttribute("stroke-width",String(i)),d.setAttribute("stroke-linecap",t.stroke?.dash==="dotted"?"round":"butt"),d.setAttribute("stroke-linejoin","round"),l&&d.setAttribute("stroke-dasharray",l)),e.append(d),t.shape==="arrow"&&r.length>=2){let[u,p]=[r[r.length-2],r[r.length-1]],m=Math.atan2(p[1]-u[1],p[0]-u[0]),y=Math.max(10,i*3.5),A=k=>`${p[0]-y*Math.cos(m)+k*y*.5*Math.sin(m)},${p[1]-y*Math.sin(m)-k*y*.5*Math.cos(m)}`;d.setAttribute("points",[...r.slice(0,-1),[p[0]-y*.8*Math.cos(m),p[1]-y*.8*Math.sin(m)]].map(k=>`${k[0]},${k[1]}`).join(" "));let f=ye("polygon");f.setAttribute("points",`${p[0]},${p[1]} ${A(1)} ${A(-1)}`),f.setAttribute("fill",a),e.append(f)}return e}async function Qt(t,e,n,s,i,o,r,a=()=>{},l){let d=t.appInfo??n.info;zt(e),d?.id&&(e.dataset.app=d.id);let u=(x,C)=>(e.dataset.appFailed=x,{failed:x,...C?{error:C}:{}});if(!Dt(d?.requires))return u("unsupported");let p=s.appHost;if(p&&!p.custom&&!oe(d))return u("unsupported");if(Ut(l,d))return qt(t,e,n,s,i,o,r,a,l,u);let m=t.app;if(p?.held?.(m))return e.dataset.appHeld="",{held:!0};await p?.mounting?.(m,d?.id);let y=!1,A=()=>{y||(y=!0,p?.unmounted?.(m))},f;try{f=await jt(n.hash,n.bytes,d)}catch(x){return A(),x instanceof F?u(x.reason,x.error):u("load")}let k=f.tag,b=d?.id?s.controllerFor?.({id:d.id,instance:t.id,capability:d.player??"read"}):void 0,M,w=new Promise(x=>{M=x}),T,N={app:d?.id??t.app.id,element:k,props:St(t.props??{},Gt(s)),place:s.place,screen:s.screen??null,timeZone:s.timeZone??Intl.DateTimeFormat().resolvedOptions().timeZone,locale:s.locale??navigator.language,preview:!!s.preview,now:i.now,startedAt:i.startedAt,ready:()=>M(),data:(x,C)=>s.data?s.data.get(x,C):Promise.reject(new Error("no data service")),kept:(x,C)=>s.data?.kept?.(x,C),image:x=>s.data?s.data.image(x):Promise.reject(new Error("no data service")),snapshot:x=>{let C=s.snapshot?.(x)??null;if(!C||t.binding?.table!==x||!Vt(t.binding))return C;let Q=Kt(C,t.binding,s.screen,N.timeZone,i.now());return T?.from===C&&T.rows.length===Q.rows.length&&T.rows.every((Rn,Nn)=>Rn===Q.rows[Nn])?T.snapshot:(T={from:C,rows:Q.rows,snapshot:Q},Q)},...t.binding?{binding:t.binding}:{},...t.sensors?.includes("impressions")&&s.impressions?.measuring?{impressions:{now:()=>s.impressions.now(),on:x=>s.impressions.on(x)}}:{},...b?{controller:b}:{}},j;try{j=D(k),f.shape==="class"?f.attach(j,N):j.wyndlass=N}catch(x){return b?.dispose(),A(),u("error",W(x))}j.style.cssText="display:block;width:100%;height:100%",p?.contained?.(e,j);let q=new Ae(j,{onStop:a,dispose:()=>{b?.dispose(),A()}});e.append(j),r(q);let G=await Promise.race([w.then(()=>!1),new Promise(x=>setTimeout(()=>x(!0),o))]);return q.update(N.now()),{app:q,late:G}}var Zt=new Set;async function en(t,e){for(let n of t){let s=`${n.family}/${n.weight??400}/${n.style??"normal"}/${n.blob}`,i=e(n.blob);if(!(Zt.has(s)||!i||typeof FontFace>"u"))try{let o=new FontFace(n.family,i,{weight:String(n.weight??400),style:n.style??"normal"});document.fonts.add(await o.load()),Zt.add(s)}catch{}}}function ae(t){let e=[],n=null;for(let s of t){let i=s.start,o=Math.max(0,i.kind==="at"?i.ms:i.kind==="withPrevious"?(n?.startMs??0)+(i.offsetMs??0):(n?.endMs??0)+(i.offsetMs??0));n={startMs:o,endMs:o+s.durationMs},e.push(n)}return{clips:e,autoDurationMs:e.reduce((s,i)=>Math.max(s,i.endMs),0)}}var si=new Set(["x","y","scaleX","scaleY","rotation","opacity","reveal"]);function ii(t,{pageBelow:e=!1}={},n=null){let s=o=>({path:"page",reason:o});if(!n)return s("no-media-layer");if(e)return s("page-element-beneath");let i=t.slice(0,-1).filter(o=>o.type==="shape"&&o.clip!==!1);if(i.some(o=>o.shape!=="rectangle"||(o.radius??0)>0))return s("clipped-to-shape");if(i.some(o=>(o.rotation??0)%360!==0))return s("clipped-by-rotated-shape");if(!n.animates){if(t.some(o=>(o.rotation??0)%360!==0))return s("rotated");if(t.some(o=>(o.opacity??1)<1))return s("translucent");if(t.some(o=>(o.animated??[]).some(r=>si.has(r))))return s("animated")}return{path:"native"}}var ri={fade:["opacity"],slide:["x","opacity"],scale:["scaleX","scaleY","opacity"],pulse:["scaleX","scaleY"],loop:["scaleX","scaleY"],wipe:["reveal"]};function tn(t,e,n,s){let i=e&&t.overrides?.[e]||{},o=new Map;for(let p of t.animation?.clips??[]){let m=[...p.preset?ri[p.preset]??[]:[],...(p.lanes??[]).map(y=>y.property)];o.set(p.element,[...o.get(p.element)??[],...m])}let r=p=>p.type==="library"&&!!p.source&&!!n[p.source.item.id]?.video,a=p=>!r(p)&&!(p.type==="shape"&&!p.fill&&!(p.stroke?.width??0)),l=[],d=(p,m,y,A)=>{for(let f of p){let k=i[f.id];if(k?.hidden??f.hidden??!1)continue;let b=k?.frame??f.frame,M={x:m.x+b.x,y:m.y+b.y,w:b.w,h:b.h},w={type:f.type==="shape"?"shape":"library",rotation:b.rotation??0,opacity:f.opacity??f.style?.opacity??1,animated:o.get(f.id)??[],...f.type==="shape"?{shape:f.shape,radius:f.radius,clip:f.clip}:{}};l.push({e:f,box:M,chain:[...y,w],ancestors:A}),f.type==="shape"&&d(f.children??[],M,[...y,w],[...A,f])}};d(t.elements,{x:0,y:0},[],[]);let u=(p,m)=>p.x<m.x+m.w&&m.x<p.x+p.w&&p.y<m.y+m.h&&m.y<p.y+p.h;return l.flatMap((p,m)=>{if(!r(p.e))return[];let y=l.slice(0,m).some(A=>a(A.e)&&u(A.box,p.box));return[{element:p.e.id,path:ii(p.chain,{pageBelow:y},s),box:p.box}]})}var oi=[0,0,1,1];function tt(t,e){if(e<=0)return 0;if(e>=1)return 1;let[n,s,i,o]=t;if(n===s&&i===o)return e;let r=3*n,a=3*(i-n)-r,l=1-r-a,d=3*s,u=3*(o-s)-d,p=1-d-u,m=w=>((l*w+a)*w+r)*w,y=w=>((p*w+u)*w+d)*w,A=w=>(3*l*w+2*a)*w+r,f=1e-7,k=e;for(let w=0;w<8;w++){let T=m(k)-e;if(Math.abs(T)<f)return y(k);let N=A(k);if(Math.abs(N)<1e-6)break;k-=T/N}let b=0,M=1;for(k=e;b<M;){let w=m(k);if(Math.abs(w-e)<f||(e>w?b=k:M=k,k=(M-b)/2+b,M-b<f))break}return y(k)}function Ee(t,e){let n=t.keyframes;if(e<=n[0].t)return n[0].v;let s=n[n.length-1];if(e>=s.t)return s.v;let i=0;for(;e>=n[i+1].t;)i++;let o=n[i],r=n[i+1];return o.v+(r.v-o.v)*tt(o.easing??oi,(e-o.t)/(r.t-o.t))}function nn(t,e,n){let s={x:t.x,y:t.y,scaleX:1,scaleY:1,rotation:0,opacity:1};for(let i of e??[])s[i.property]=Ee(i,n);return s}var de={linear:[0,0,1,1],ease:[.25,.1,.25,1],"ease-in":[.42,0,1,1],"ease-out":[0,0,.58,1],"ease-in-out":[.42,0,.58,1]};function sn(t){if(!t)return de.ease;if(de[t])return de[t];let n=/^cubic-bezier\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)$/.exec(t)?.slice(1).map(Number);return n&&n.every(Number.isFinite)?n:de.ease}function ai(t){let e=typeof t=="string"?/^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})?$/.exec(t):null;if(!e)return null;let n=parseInt(e[1],16);return[n>>16&255,n>>8&255,n&255,e[2]?parseInt(e[2],16)/255:1]}var an={fade:()=>({opacity:0}),slide:t=>({x:t.x-t.w*.1,opacity:0}),scale:()=>({scaleX:.8,scaleY:.8,opacity:0}),wipe:()=>({reveal:0}),typewriter:()=>({chars:0}),"count-up":()=>({count:0})},Zo=Object.keys(an);var di={x:t=>t.x,y:t=>t.y,scaleX:()=>1,scaleY:()=>1,rotation:()=>0,opacity:()=>1,reveal:()=>1,chars:()=>1,count:()=>1},rn=1e3,li=de["ease-in-out"];function dn(t,e){let n=ae(t).clips,s=new Map,i=new Set,o=a=>{let l=s.get(a);return l||s.set(a,l={keys:new Map,colour:null,loops:[],tickers:[]}),l},r=(a,l,d)=>{let u=o(a).keys,p=u.get(l)??[];u.set(l,p),p.push(d)};t.forEach((a,l)=>{let d=e.get(a.element);if(!d)return;let{startMs:u,endMs:p}=n[l],m=sn(a.easing);if(a.lanes?.length){for(let f of a.lanes)for(let k of f.keyframes){let b={t:u+k.at,easing:sn(k.easing??a.easing)};if(f.property==="color"){let M=ai(k.value);if(!M)continue;let w=o(a.element);w.colour??(w.colour=[[],[],[],[]]),M.forEach((T,N)=>w.colour[N].push({...b,v:T}))}else r(a.element,f.property,{...b,v:Number(k.value)})}return}let y=a.preset??"fade";if(a.kind==="emphasis"){y==="pulse"?(r(a.element,"pulse",{t:u,v:1,easing:m}),r(a.element,"pulse",{t:(u+p)/2,v:1.05,easing:m}),r(a.element,"pulse",{t:p,v:1})):y==="loop"?o(a.element).loops.push({startMs:u,endMs:p}):y==="ticker"?o(a.element).tickers.push({startMs:u,endMs:p}):i.add(y);return}let A=an[y];if(!A){i.add(y);return}for(let[f,k]of Object.entries(A(d))){let b=di[f](d),[M,w]=a.kind==="enter"?[k,b]:[b,k];r(a.element,f,{t:u,v:M,easing:m}),r(a.element,f,{t:p,v:w})}});for(let a of s.values()){for(let l of a.keys.values())l.sort((d,u)=>d.t-u.t);for(let l of a.colour??[])l.sort((d,u)=>d.t-u.t)}return{motions:s,unplayed:[...i].sort()}}var pi=["x","y","scaleX","scaleY","rotation","opacity"];function ln(t,e,n,s=!1){if(s)return ci(t,e,n);let i=(l,d)=>{let u=t?.keys.get(l);return u?Ee({property:"x",keyframes:u},n):d},o=nn(e,pi.flatMap(l=>t?.keys.get(l)?[{property:l,keyframes:t.keys.get(l)}]:[]),n),r=i("pulse",1);for(let l of t?.loops??[])if(n>=l.startMs&&n<l.endMs){let d=(n-l.startMs)%rn/rn;r*=1+.05*tt(li,d<.5?d*2:2-d*2)}let a=t?.tickers.find(l=>n>=l.startMs&&n<l.endMs);return{...o,reveal:i("reveal",1),chars:i("chars",1),count:i("count",1),pulse:r,scroll:a?(n-a.startMs)/(a.endMs-a.startMs):null,color:t?.colour?t.colour.map(l=>Ee({property:"x",keyframes:l},n)):null}}function on(t,e){if(e<t[0].t)return t[0].v;let n=0;for(;n+1<t.length&&t[n+1].t<=e;)n++;return n+1<t.length?t[n+1].v:t[n].v}function ci(t,e,n){let s=(i,o)=>{let r=t?.keys.get(i);return r?on(r,n):o};return{x:s("x",e.x),y:s("y",e.y),scaleX:s("scaleX",1),scaleY:s("scaleY",1),rotation:s("rotation",0),opacity:s("opacity",1),reveal:s("reveal",1),chars:s("chars",1),count:s("count",1),pulse:1,scroll:null,color:t?.colour?t.colour.map(i=>on(i,n)):null}}function ui(t,e){let n=Array.from(t);return e>=1?t:n.slice(0,Math.max(0,Math.round(e*n.length))).join("")}function hi(t,e){return e>=1?t:t.replace(/\d+(?:\.\d+)?/g,n=>(Number(n)*Math.max(0,e)).toFixed((n.split(".")[1]??"").length))}function pn(t,e,n){if(t<=1||e<=0)return[{page:0,startMs:0,endMs:n}];let s=[];for(let i=0;i*e<n&&i<1e3;i++)s.push({page:i%t,startMs:i*e,endMs:Math.min((i+1)*e,n)});return s}function cn(t,e,n,s){t.style.left=`${n.x}px`,t.style.top=`${n.y}px`,t.style.transform=`rotate(${(e.frame.rotation??0)+n.rotation}deg) scale(${n.scaleX*n.pulse}, ${n.scaleY*n.pulse})`,t.style.opacity=String((e.opacity??e.style?.opacity??1)*n.opacity),t.style.clipPath=n.reveal>=1?"":`inset(0 ${(1-Math.max(0,n.reveal))*100}% 0 0)`;let i=n.color?`rgba(${n.color.slice(0,3).map(o=>Math.round(o)).join(",")},${n.color[3]})`:null;if(e.type==="text"&&s!==void 0){let o=hi(ui(s,n.chars),n.count);if(n.scroll!==null){let r=t.querySelector("[data-ticker]");r||(r=document.createElement("span"),r.dataset.ticker="",r.style.cssText="display:inline-block;white-space:nowrap;will-change:transform",t.replaceChildren(r),t.style.overflow="hidden"),r.textContent!==o&&(r.textContent=o);let a=e.frame.w,l=r.scrollWidth;r.style.transform=`translateX(${a-n.scroll*(a+l)}px)`}else(t.textContent!==o||t.querySelector("[data-ticker]"))&&(t.textContent=o,t.style.overflow="");i&&(t.style.color=i)}else if(e.type==="shape"&&i){let o=t.querySelector("polygon, polyline");o&&e.shape==="polygon"?o.setAttribute("fill",i):o||(t.style.background=i)}}function mi(t){let e=t>>>0;return()=>{e=e+1831565813|0;let n=Math.imul(e^e>>>15,1|e);return n=n+Math.imul(n^n>>>7,61|n)^n,((n^n>>>14)>>>0)/4294967296}}async function un(t,e,n,s){let i=parseInt((await We(_e(`${t}
|
|
2
|
+
"use strict";(()=>{var qn=Object.defineProperty;var _n=(t,e,n)=>e in t?qn(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var h=(t,e,n)=>_n(t,typeof e!="symbol"?e+"":e,n);var At=Reflect.apply,Oe=Object.getOwnPropertyDescriptor,ge=Object.getPrototypeOf,R=globalThis,x=(t,e,n)=>At(t,e,n),O=(t,e)=>t?Oe(t,e)?.get:void 0,$e=(t,e)=>t?Oe(t,e)?.set:void 0,L=t=>R[t]?.prototype,Et=(t,e,n)=>At(t,e,n);var Kr=Date.now,Zr=new TextEncoder,Jr=new TextDecoder,Yr=TextEncoder.prototype.encode,Xr=TextDecoder.prototype.decode;var Gr=O(ge(Uint8Array.prototype),"length");var Wn=Uint8Array,zn=t=>new Wn(t),Vn=[];for(let t=0;t<256;t++)Vn[t]=(t<16?"0":"")+t.toString(16);var Kn=Object.create(null);for(let t=0;t<64;t++)Kn["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[t]]=t;var ht=R.crypto?.subtle,De=ht?ge(ht):void 0,Ne=R.crypto,Zn=Ne?ge(Ne).getRandomValues:void 0,Qr=De?.digest,ei=De?.importKey,ti=De?.sign;var St=t=>x(Zn,Ne,[zn(t)]),ni={setTimeout:R.setTimeout,clearTimeout:R.clearTimeout,setInterval:R.setInterval,clearInterval:R.clearInterval};var si=R.fetch;var se=L("MessagePort"),ri=se&&se.postMessage,ii=$e(se,"onmessage"),oi=se&&se.start,je=L("MessageEvent"),ai=O(je,"data"),di=O(je,"ports"),li=O(je,"source");var Tt=L("EventTarget"),pi=Tt?.addEventListener,ci=Tt?.removeEventListener;var ui=R.Worker,me=L("Worker"),mi=me&&me.postMessage,hi=$e(me,"onmessage"),fi=$e(me,"onerror");var ft=R.Blob,he=R.URL,gt=he?.createObjectURL,Jn=he?.revokeObjectURL,Fe=ft&>?{make:(t,e)=>x(gt,he,[new ft(t,{type:e})]),revoke:t=>x(Jn,he,[t])}:void 0,te=R.customElements,He=te?ge(te):void 0,Yn=He?.define,Xn=He?.get,Be=te&&He?{define:(t,e)=>x(Yn,te,[t,e]),get:t=>x(Xn,te,[t])}:void 0,Pt=R.document,fe=L("Document"),Gn=fe?.createElement,Qn=fe?.createElementNS,es=WeakSet.prototype.add,gi=WeakSet.prototype.has,Ue=new WeakSet,ts=new WeakSet,ye=(t,e)=>(x(es,t,[e]),e);function j(t){return ye(Ue,x(Gn,Pt,[t]))}var be=t=>ye(Ue,x(Qn,Pt,["http://www.w3.org/2000/svg",t]));var Z=t=>ye(ts,t);var re=L("Node"),qe=L("Element"),we=L("HTMLElement"),J=t=>O(re,t),yt=J("parentNode"),ns=J("firstChild"),ss=J("lastChild"),rs=J("nextSibling"),is=J("nodeType"),os=J("childNodes"),as=O(qe,"localName"),ds=re?.appendChild,ls=re?.insertBefore,ps=re?.removeChild,z=qe,cs=z?.getAttribute,us=z?.getAttributeNames,ms=z?.setAttribute,hs=z?.removeAttribute,fs=z?.matches,gs=z?.attachShadow,ys=z?.querySelectorAll,bs=L("HTMLDialogElement")?.close,ws=O(L("NodeList"),"length"),vs=L("NodeList")?.item,ve=L("MutationRecord"),ks=O(ve,"type"),xs=O(ve,"target"),Ms=O(ve,"addedNodes"),As=O(ve,"removedNodes"),bt=R.MutationObserver,Es=L("MutationObserver")?.observe,Lt=fe?Oe(fe,"adoptedStyleSheets"):void 0,wt=Lt?.get,vt=Lt?.set,Ss=R.requestAnimationFrame,kt=O(we,"style"),ke=L("CSSStyleDeclaration"),Ts=ke?.getPropertyValue,Ps=ke?.getPropertyPriority,Ls=ke?.setProperty,xt=we?.showPopover,Mt=we?.hidePopover,Is=O(L("Event"),"target"),Cs=L("Event")?.stopImmediatePropagation,ue=t=>{let e=[];for(let n=0,s=x(ws,t,[]);n<s;n++)e[n]=x(vs,t,[n]);return e},ne=bt&&re&&qe&&we&&ke?{parent:t=>x(yt,t,[]),first:t=>x(ns,t,[]),last:t=>x(ss,t,[]),next:t=>x(rs,t,[]),isElement:t=>x(is,t,[])===1,name:t=>x(as,t,[]),children:t=>ue(x(os,t,[])),append:(t,e)=>x(ds,t,[e]),insert:(t,e,n)=>x(ls,t,[e,n]),remove:t=>{let e=x(yt,t,[]);e&&x(ps,e,[t])},attribute:(t,e)=>x(cs,t,[e]),attributes:t=>x(us,t,[]),setAttribute:(t,e,n)=>x(ms,t,[e,n]),removeAttribute:(t,e)=>x(hs,t,[e]),matches:(t,e)=>x(fs,t,[e]),all:(t,e)=>ue(x(ys,t,[e])),closeDialog:t=>x(bs,t,[]),shadow:t=>x(gs,t,[{mode:"closed"}]),style:(t,e)=>{let n=x(kt,t,[]);return[x(Ts,n,[e]),x(Ps,n,[e])]},setStyle:(t,e,n,s="")=>x(Ls,x(kt,t,[]),[e,n,s]),observer:t=>new bt(t),observe:(t,e,n)=>x(Es,t,[e,n]),record:t=>({type:x(ks,t,[]),target:x(xs,t,[]),added:ue(x(Ms,t,[])),removed:ue(x(As,t,[]))}),adopted:wt&&vt?{count:t=>x(wt,t,[]).length,clear:t=>x(vt,t,[[]])}:void 0,frame:t=>x(Ss,globalThis,[t]),popover:xt&&Mt?{show:t=>x(xt,t,[]),hide:t=>x(Mt,t,[])}:void 0,target:t=>x(Is,t,[]),stop:t=>x(Cs,t,[])}:void 0;function _e(t){if(ye(Ue,t),ne)for(let e of ne.children(t))ne.isElement(e)&&_e(e);return t}var We=t=>new TextEncoder().encode(t);function Rs(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 ze(t){return Rs(await crypto.subtle.digest("SHA-256",t))}var Ns=["name","type","tags","location","site","district","region","organization","path","timeZone"],Os=["text","date","currency","truncate"],It={site:"SITE",district:"DISTRICT",region:"REGION",organization:"ORGANIZATION"};function xe(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 It[n]?[...r].reverse().find(o=>o.kind===It[n])?.name:void 0}}function $s(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 Ve(t,e){return(t.source==="screen"?xe(e.screen,e.timeZone,t.field,t.key):$s(e.tables?.[t.table]?.rows?.[t.row??0]?.[t.column],t.format))??t.fallback??""}function Ds(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}:{}}:Ns.includes(e[1])?{source:"screen",field:e[1],...s!==void 0?{fallback:s}:{}}:null:r&&!Os.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 Ke(t,e){return t.replace(/\{{2}([^{}]*)\}{2}/g,(n,s)=>{let r=Ds(s);return r?Ve(r,e):n})}function Ct(t,e){return Object.fromEntries(Object.entries(t).map(([n,s])=>[n,typeof s=="string"?Ke(s,e):s]))}var Ze=t=>t==null||t==="";function Nt(t,e,n,s,r,o){return js(t,e,n,s,{now:r,timeZone:o})}function js(t,e,n,s,r){if(n===null&&(e==="eq"||e==="ne"))return e==="eq"?Ze(t):!Ze(t);if(Ze(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)&&Je(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"?Fs(r.now,r.timeZone).date:String(n),i=String(t).slice(0,10);return/^\d{4}-\d{2}-\d{2}$/.test(o)&&Je(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)&&Je(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 Je(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 Rt=new Map;function Fs(t,e){let n=Rt.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"}),Rt.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 Ot=(t,e)=>!t||(t.from===void 0||t.from<=e)&&(t.until===void 0||e<t.until);var ie=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 Ye=class extends Error{constructor(n,s){super(s);h(this,"code",n);this.name="MediaError"}};function Xe(t){return t instanceof Ye?{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 $t=t=>t.blob.contentType.startsWith("video/");var Ge="position:absolute;inset:0;width:100%;height:100%;object-fit:contain",Bs=1,Me=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?jt(e):void 0;if(n.kind==="layout"&&o===void 0){await B(()=>{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 B(()=>{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 B(()=>{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(()=>{}),Us(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 B(()=>{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"),Ft(s.el,1,0,o).then(()=>{s.el.remove(),this.dispose(s)}),"crossfade"}else if(o!==void 0)return await B(()=>{n.el.style.visibility="",n.el.style.opacity="0",this.container.append(n.el),n.kind==="video"&&Qe(n.el),this.shown=n,this.shownHash=e.blob.hash}),n.view&&(this.held=[],n.view.start().then(()=>this.releaseHeld())),Ft(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 B(()=>{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 B(()=>{this.shown===n&&(s?.el.remove(),this.paint("transparent"))})}else{let i=Promise.resolve();await B(()=>{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"&&Qe(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"?Qe(e.el):e?.mediaId&&this.options.media?.control(e.mediaId,"play").catch(()=>{})}make(e){if(e.layout){let{doc:i,source:a,untilPass:p}=e.layout,l=Z(j("div"));l.dataset.blob=e.blob.hash,l.style.cssText=`${Ge};visibility:hidden`,this.container.append(l);let c={slide:e,kind:"layout",el:l,ready:Promise.resolve()},d=g=>this.options.path?`${this.options.path}/${g}`:g,m=String(++this.drawn);return c.view=new Y(i,a,l,{media:this.options.media,path:this.options.path,endsWith:p?d(p):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(p&&g===d(p)&&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(!$t(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=Z(j("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-${Bs++}`,a=Z(j("div"));a.dataset.blob=e.blob.hash,a.dataset.media=i,a.style.cssText=`${Ge};visibility:hidden`;let p=this.container.getBoundingClientRect(),l={x:p.left,y:p.top,w:p.width||window.innerWidth,h:p.height||window.innerHeight},c=jt(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}]}]:[],g={slide:e,kind:"native",el:a,mediaId:i,fadesIn:d,ready:Promise.resolve()},w=this.options.fit??"contain";return g.ready=n.prepare({mediaId:i,blob:e.blob.hash,contentType:e.blob.contentType,box:l,z:d?1:0,loop:e.loop,muted:!1,fit:w,...d?{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=Z(j("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 Ge.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 jt(t){if(t.transition?.type==="crossfade")return Math.max(0,Math.min(t.transition.durationMs??1e3,t.durationMs/2))}var Us=t=>new Promise(e=>setTimeout(e,t));function Ft(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 p=()=>{if(i)return;let l=Math.min(1,(Date.now()-o)/s);if(l>=1)return a();t.style.opacity=String(e+(n-e)*l),requestAnimationFrame(p)};requestAnimationFrame(p),setTimeout(a,s+250)})}function Qe(t){t.play().catch(()=>(t.muted=!0,t.play())).catch(()=>{})}function B(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 oe={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 Ht=Symbol.for("wyndlass.app.attach");var H=class extends Error{constructor(n,s){super(`the app could not run (${n})`);h(this,"reason",n);h(this,"error",s)}};function V(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 ae(t){let e=t?.id;if(typeof e!="string"||e==="")return!0;for(let n=0;n<Bt.length;n++)if(e[n]!==Bt[n])return!1;return!0}var Bt="com.wyndlass.";function _s(t=typeof navigator>"u"?"":navigator.userAgent){let e=/Chrom(?:e|ium)\/(\d+)/.exec(t);return e?Number(e[1]):void 0}function Ut(t,e=oe.apiVersion,n=_s()){return!(t?.playerApi!==void 0&&t.playerApi>e||t?.webView!==void 0&&n!==void 0&&t.webView>n)}function Ws(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 et=new Map;function zs(){let t=St(8),e="";for(let n=0;n<8;n++)e+=Vs[t[n]%26];return e}var Vs="abcdefghijklmnopqrstuvwxyz",Ks=0;function qt(t,e,n){let s=et.get(t);if(!s){let r=Fe.make([e],"text/javascript");s=import(r).then(o=>Zs(o,n),o=>{throw o instanceof H?o:new H("load",V(o))}).finally(()=>Fe.revoke(r)),et.set(t,s),s.catch(()=>et.delete(t))}return s}function Zs(t,e){let n=t.default;if(typeof n=="function"&&n.prototype instanceof HTMLElement){let s=n.prototype[Ht];if(typeof s!="function")throw new H("shape");let r=class extends n{},o=`${e?.element??"wl-app"}-${zs()}${++Ks}`;try{Be.define(o,r)}catch(i){throw new H("shape",V(i))}return{tag:o,shape:"class",attach:(i,a)=>{Et(s,i,[a])}}}if(ae(e)&&typeof t.element=="string"&&Be.get(t.element))return{tag:t.element,shape:"legacy"};throw new H("shape")}var Js=3,Ys=6e4,Ae=class{constructor(e,n){h(this,"element",e);h(this,"o",n);h(this,"faults",[]);h(this,"halted",!1)}get stopped(){return this.halted}has(e){try{return!this.halted&&typeof this.element[e]=="function"}catch{return!1}}update(e){this.halted||this.run(()=>this.element.update?.(e))}reset(){this.halted||this.run(()=>this.element.reset?.())}paging(){return this.halted?null:Ws(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<Ys),this.faults.push(n),this.faults.length>=Js&&this.stop(V(e))}},Xs=24*3600*1e3,Ee=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 p=this.stored(r),l=p?JSON.parse(p):void 0;if(l&&Date.parse(l.expiresAt)>this.now())return this.dress(l,!1);let c=this.url(`/v0/${encodeURIComponent(e)}?${s}`);try{if(!c)throw new Error("no data service");let 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(l&&Date.parse(l.expiresAt)+Xs>this.now())return this.dress(l,!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 _t="wyndlass.frame";function Vt(t,e){return!!t&&(t.apps?t.apps(e):!ae(e))}var Gs=new Set(["load","shape","unsupported","error","late","overran"]),Wt=50,zt=250,tt=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)))<=Wt:e===r.t)){r.running&&this.watch();return}let o=!!s&&n-s.at<zt&&e>s.t&&Math.abs(e-s.t-(n-s.at))<=Wt;this.told={t:e,at:n,running:o},this.post({type:"clock",t:e,running:o}),o&&this.watch()}reset(){this.halted||this.post({type:"reset"})}hook(e,n){if(this.halted)return Promise.resolve();let s=++this.next;return new Promise(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(){clearTimeout(this.quiet),this.port.close();for(let e of this.pending.values())e();this.pending.clear()}heard(e){e.type==="paging"?this.pages=Zt(e.paging):e.type==="hooked"&&typeof e.id=="number"?(this.pending.get(e.id)?.(),this.pending.delete(e.id)):e.type==="stopped"&&this.stop(Jt(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}))},zt)}};async function Kt(t,e,n,s,r,o,i,a,p,l){let c=t.appInfo??n.info,d;try{d=new URL(p.frames).origin}catch{return l("unsupported")}let m=document.createElement("iframe");m.setAttribute("sandbox","allow-scripts"),m.setAttribute("referrerpolicy","no-referrer"),m.title=c?.id??"app",m.tabIndex=-1,m.dataset.frame=c?.id??"",m.style.cssText="display:block;width:100%;height:100%;border:0;pointer-events:none;background:transparent;color-scheme:normal";let g=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(),l(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:tr(t.props??{},p),snapshots:nr(t,s),deadlineMs:o};return new Promise(A=>{let k,P=!1,N=Q=>{P||(P=!0,clearTimeout(F),A(Q))},F=setTimeout(()=>{k?(N({app:k,late:!0}),k.update(r.now())):(f.close(),N(w("load")))},o+1e3),W=!1;f.onmessage=Q=>{let M=Q.data;if(!(!M||typeof M!="object")){if(M.type==="ask"){W&&Qs(M,s,f);return}if(k){M.type==="ready"?(N({app:k,late:M.late===!0}),k.update(r.now())):k.heard(M);return}M.type==="hello"&&!W?(W=!0,f.postMessage(v,[v.bundle.bytes.buffer])):M.type==="made"&&Array.isArray(M.hooks)?(k=new tt(m,f,{type:"made",hooks:M.hooks.filter(C=>C==="enter"||C==="exit"),paging:Zt(M.paging)},{onStop:a}),i(k)):M.type==="failed"&&(f.close(),N(w(Gs.has(M.reason)?M.reason:"error",Jt(M.error))))}},m.contentWindow.postMessage({type:_t},"*",[b])})}async function Qs(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,er(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:V(r)??"Error"})}}function er(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 Zt(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 Jt(t){return typeof t=="string"&&/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(t)?t:void 0}function tr(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 nr(t,e){let n=new Set;t.binding?.table&&n.add(t.binding.table);for(let r of Object.values(t.props??{}))typeof r=="string"&&/^tbl_[0-9a-z]{26}$/.test(r)&&n.add(r);let s={};for(let r of n){let o=e.snapshot?.(r);o&&(s[r]=o)}return s}function Yt(t){let e=ne?.setStyle??((n,s,r,o)=>n.style.setProperty(s,r,o));e(t,"contain","strict","important"),e(t,"overflow","clip","important")}var Xt=t=>!!(t?.filter?.length||t?.sort||t?.limit),sr=t=>t==null||t==="";function rr(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 ir(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 or(t,e,n,s,r){let o=t,i=Array.isArray(o?.rows)?o.rows:[],a=Array.isArray(o?.columns)?o.columns:void 0,p=l=>a?.find(c=>c.key===l)?.type??"text";for(let l of e.filter??[]){if(a&&!a.some(m=>m.key===l.column))return[];let c=l.value;if(c!==null&&typeof c=="object"){let m=c;if(c=xe(n,s,m.screen,m.key),c===void 0)return[]}let d=p(l.column);i=i.filter(m=>Nt(m[l.column],l.op,c,d,r,s))}if(e.sort){let{column:l}=e.sort,c=p(l),d=e.sort.order==="desc"?-1:1;i=i.map((m,g)=>({row:m,i:g,key:rr(m[l],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:ir(String(m.key),String(g.key));return w===0?m.i-g.i:w<0?-d:d}).map(m=>m.row)}return e.limit?i.slice(0,e.limit):i}function Gt(t,e,n,s,r){return!t||typeof t!="object"?t:{...t,rows:or(t,e,n,s,r)}}function ar(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 en(t,e,n,s=[]){let r=j("div");if(r.dataset.background="",r.style.cssText="position:absolute;inset:0;pointer-events:none",r.style.background=ar(e),s.length){let o=[`M0 0H${n.width}V${n.height}H0Z`,...s.map(p=>`M${p.x} ${p.y}H${p.x+p.w}V${p.y+p.h}H${p.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[p,l]of[["maskImage",a],["maskSize","100% 100%"],["maskRepeat","no-repeat"]])r.style[p]=l,r.style[`webkit${p[0].toUpperCase()}${p.slice(1)}`]=l}return t.prepend(r),r}function tn(t,e=t.frame){let n=j("div");return n.dataset.element=t.id,n.style.cssText="position:absolute;box-sizing:border-box",dr(n,e),n}function dr(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 nn(t,e,n){if(e.type==="text"){let s=e.style??{},r=sn(n);t.textContent=e.bind?Ve(e.bind,r):Ke(e.text??"",r),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"&&lr(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 sn(t){return{screen:t?.screen??null,timeZone:t?.timeZone,tables:new Proxy({},{get:(e,n)=>typeof n=="string"?t?.snapshot?.(n):void 0})}}var lr=t=>t.shape==="line"||t.shape==="arrow"||t.shape==="polygon";function pr(t){let e=be("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",p=t.stroke?.dash==="dashed"?`${r*3} ${r*2}`:t.stroke?.dash==="dotted"?`0 ${r*2}`:"",l=be(t.shape==="polygon"?"polygon":"polyline");if(l.setAttribute("points",i.map(c=>`${c[0]},${c[1]}`).join(" ")),l.setAttribute("fill",t.shape==="polygon"?t.fill?.value??"none":"none"),r&&(l.setAttribute("stroke",a),l.setAttribute("stroke-width",String(r)),l.setAttribute("stroke-linecap",t.stroke?.dash==="dotted"?"round":"butt"),l.setAttribute("stroke-linejoin","round"),p&&l.setAttribute("stroke-dasharray",p)),e.append(l),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]),g=Math.max(10,r*3.5),w=b=>`${d[0]-g*Math.cos(m)+b*g*.5*Math.sin(m)},${d[1]-g*Math.sin(m)-b*g*.5*Math.cos(m)}`;l.setAttribute("points",[...i.slice(0,-1),[d[0]-g*.8*Math.cos(m),d[1]-g*.8*Math.sin(m)]].map(b=>`${b[0]},${b[1]}`).join(" "));let f=be("polygon");f.setAttribute("points",`${d[0]},${d[1]} ${w(1)} ${w(-1)}`),f.setAttribute("fill",a),e.append(f)}return e}async function rn(t,e,n,s,r,o,i,a=()=>{},p){let l=t.appInfo??n.info;Yt(e),l?.id&&(e.dataset.app=l.id);let c=(M,C)=>(e.dataset.appFailed=M,{failed:M,...C?{error:C}:{}});if(!Ut(l?.requires))return c("unsupported");let d=s.appHost;if(d&&!d.custom&&!ae(l))return c("unsupported");if(Vt(p,l))return Kt(t,e,n,s,r,o,i,a,p,c);let m=t.app;if(d?.held?.(m))return e.dataset.appHeld="",{held:!0};await d?.mounting?.(m,l?.id);let g=!1,w=()=>{g||(g=!0,d?.unmounted?.(m))},f;try{f=await qt(n.hash,n.bytes,l)}catch(M){return w(),M instanceof H?c(M.reason,M.error):c("load")}let b=f.tag,v=l?.id?s.controllerFor?.({id:l.id,instance:t.id,capability:l.player??"read"}):void 0,A,k=new Promise(M=>{A=M}),P,N={app:l?.id??t.app.id,element:b,props:Ct(t.props??{},sn(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,C)=>s.data?s.data.get(M,C):Promise.reject(new Error("no data service")),kept:(M,C)=>s.data?.kept?.(M,C),image:M=>s.data?s.data.image(M):Promise.reject(new Error("no data service")),snapshot:M=>{let C=s.snapshot?.(M)??null;if(!C||t.binding?.table!==M||!Xt(t.binding))return C;let ee=Gt(C,t.binding,s.screen,N.timeZone,r.now());return P?.from===C&&P.rows.length===ee.rows.length&&P.rows.every((Bn,Un)=>Bn===ee.rows[Un])?P.snapshot:(P={from:C,rows:ee.rows,snapshot:ee},ee)},...t.binding?{binding:t.binding}:{},...t.sensors?.includes("impressions")&&s.impressions?.measuring?{impressions:{now:()=>s.impressions.now(),on:M=>s.impressions.on(M)}}:{},...v?{controller:v}:{}},F;try{F=j(b),f.shape==="class"?f.attach(F,N):F.wyndlass=N}catch(M){return v?.dispose(),w(),c("error",V(M))}F.style.cssText="display:block;width:100%;height:100%",d?.contained?.(e,F);let W=new Ae(F,{onStop:a,dispose:()=>{v?.dispose(),w()}});e.append(F),i(W);let Q=await Promise.race([k.then(()=>!1),new Promise(M=>setTimeout(()=>M(!0),o))]);return W.update(N.now()),{app:W,late:Q}}var Qt=new Set;async function on(t,e){for(let n of t){let s=`${n.family}/${n.weight??400}/${n.style??"normal"}/${n.blob}`,r=e(n.blob);if(!(Qt.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()),Qt.add(s)}catch{}}}function de(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 cr=new Set(["x","y","scaleX","scaleY","rotation","opacity","reveal"]);function ur(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=>cr.has(i))))return s("animated")}return{path:"native"}}var mr={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?mr[d.preset]??[]:[],...(d.lanes??[]).map(g=>g.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)),p=[],l=(d,m,g,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}:{}};p.push({e:f,box:A,chain:[...g,k],ancestors:w}),f.type==="shape"&&l(f.children??[],A,[...g,k],[...w,f])}};l(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 p.flatMap((d,m)=>{if(!i(d.e))return[];let g=p.slice(0,m).some(w=>a(w.e)&&c(w.box,d.box));return[{element:d.e.id,path:ur(d.chain,{pageBelow:g},s),box:d.box}]})}var hr=[0,0,1,1];function nt(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,p=1-i-a,l=3*s,c=3*(o-s)-l,d=1-l-c,m=k=>((p*k+a)*k+i)*k,g=k=>((d*k+c)*k+l)*k,w=k=>(3*p*k+2*a)*k+i,f=1e-7,b=e;for(let k=0;k<8;k++){let P=m(b)-e;if(Math.abs(P)<f)return g(b);let N=w(b);if(Math.abs(N)<1e-6)break;b-=P/N}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 Se(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)*nt(o.easing??hr,(e-o.t)/(i.t-o.t))}function dn(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]=Se(r,n);return s}var le={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 ln(t){if(!t)return le.ease;if(le[t])return le[t];let n=/^cubic-bezier\(\s*([^,]+),\s*([^,]+),\s*([^,]+),\s*([^)]+)\)$/.exec(t)?.slice(1).map(Number);return n&&n.every(Number.isFinite)?n:le.ease}function fr(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 un={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})},ra=Object.keys(un);var gr={x:t=>t.x,y:t=>t.y,scaleX:()=>1,scaleY:()=>1,rotation:()=>0,opacity:()=>1,reveal:()=>1,chars:()=>1,count:()=>1},pn=1e3,yr=le["ease-in-out"];function mn(t,e){let n=de(t).clips,s=new Map,r=new Set,o=a=>{let p=s.get(a);return p||s.set(a,p={keys:new Map,colour:null,loops:[],tickers:[]}),p},i=(a,p,l)=>{let c=o(a).keys,d=c.get(p)??[];c.set(p,d),d.push(l)};t.forEach((a,p)=>{let l=e.get(a.element);if(!l)return;let{startMs:c,endMs:d}=n[p],m=ln(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:ln(b.easing??a.easing)};if(f.property==="color"){let A=fr(b.value);if(!A)continue;let k=o(a.element);k.colour??(k.colour=[[],[],[],[]]),A.forEach((P,N)=>k.colour[N].push({...v,v:P}))}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+d)/2,v:1.05,easing:m}),i(a.element,"pulse",{t:d,v:1})):g==="loop"?o(a.element).loops.push({startMs:c,endMs:d}):g==="ticker"?o(a.element).tickers.push({startMs:c,endMs:d}):r.add(g);return}let w=un[g];if(!w){r.add(g);return}for(let[f,b]of Object.entries(w(l))){let v=gr[f](l),[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 p of a.keys.values())p.sort((l,c)=>l.t-c.t);for(let p of a.colour??[])p.sort((l,c)=>l.t-c.t)}return{motions:s,unplayed:[...r].sort()}}var br=["x","y","scaleX","scaleY","rotation","opacity"];function hn(t,e,n,s=!1){if(s)return wr(t,e,n);let r=(p,l)=>{let c=t?.keys.get(p);return c?Se({property:"x",keyframes:c},n):l},o=dn(e,br.flatMap(p=>t?.keys.get(p)?[{property:p,keyframes:t.keys.get(p)}]:[]),n),i=r("pulse",1);for(let p of t?.loops??[])if(n>=p.startMs&&n<p.endMs){let l=(n-p.startMs)%pn/pn;i*=1+.05*nt(yr,l<.5?l*2:2-l*2)}let a=t?.tickers.find(p=>n>=p.startMs&&n<p.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(p=>Se({property:"x",keyframes:p},n)):null}}function cn(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 wr(t,e,n){let s=(r,o)=>{let i=t?.keys.get(r);return i?cn(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=>cn(r,n)):null}}function vr(t,e){let n=Array.from(t);return e>=1?t:n.slice(0,Math.max(0,Math.round(e*n.length))).join("")}function kr(t,e){return e>=1?t:t.replace(/\d+(?:\.\d+)?/g,n=>(Number(n)*Math.max(0,e)).toFixed((n.split(".")[1]??"").length))}function fn(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 gn(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=kr(vr(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,p=i.scrollWidth;i.style.transform=`translateX(${a-n.scroll*(a+p)}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 xr(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 yn(t,e,n,s){let r=parseInt((await ze(We(`${t}
|
|
3
3
|
${e}
|
|
4
|
-
${n}`))).slice(0,8),16),o=mi(i),r=Array.from({length:s},(a,l)=>l);for(let a=s-1;a>0;a--){let l=Math.floor(o()*(a+1));[r[a],r[l]]=[r[l],r[a]]}return r}var fi=250,gi=1e3;function Ct(t,e){let n=t.animation?.duration;if(n?.kind==="fixed")return{durationMs:n.ms};if(n?.kind==="untilPlaylistEnds"){let s=fn(t.elements,n.element);return{durationMs:(s?.source?.kind==="playlist"?e.playlist(s.source.item)??[]:s?.source?[{item:s.source.item}]:[]).reduce((r,a)=>r+(e.slide(a,{imageDurationMs:s?.playback?.imageDurationMs})?.durationMs??0),0)||1e4,untilPass:n.element}}return{durationMs:ae(t.animation?.clips??[]).autoDurationMs}}function mn(t,e,n=0){let s={},i=o=>{for(let r of o){if(r.type==="library"&&r.source){let a=r.source.kind==="playlist"?e.playlist(r.source.item)??[]:[{item:r.source.item}];s[r.source.item.id]={video:a.some(l=>{let d=e.slide(l);if(!d)return!1;if(d.blob.contentType.startsWith("video/"))return!0;let u=d.layout;return!!u&&n<4&&Object.values(mn(u.doc,u.source,n+1)).some(p=>p.video)})}}r.children&&i(r.children)}};return i(t.elements),s}function fn(t,e){for(let n of t){if(n.id===e)return n;let s=n.children&&fn(n.children,e);if(s)return s}}var J=class{constructor(e,n,s,i={}){h(this,"source",n);h(this,"o",i);h(this,"el");h(this,"ready");h(this,"unplayed");h(this,"doc");h(this,"motions");h(this,"native");h(this,"animated",[]);h(this,"loops",[]);h(this,"startedAt",0);h(this,"frame",0);h(this,"running",!1);h(this,"disposed",!1);h(this,"apps",[]);h(this,"heldAt");h(this,"late",[]);h(this,"overran",[]);h(this,"timers",[]);h(this,"reduced",typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches);let o=s.clientWidth||window.innerWidth,r=s.clientHeight||window.innerHeight,a=!e.profiles[0].width,l=a?{...e.profiles[0],width:o,height:r}:e.profiles[0];a&&(e={...e,elements:e.elements.map(b=>b.frame.w?b:{...b,frame:{x:0,y:0,w:o,h:r}})});let d=Math.min(o/l.width,r/l.height);this.el=D("div"),this.el.dataset.layout=l.id,this.el.style.cssText=`position:absolute;left:${(o-l.width*d)/2}px;top:${(r-l.height*d)/2}px;width:${l.width}px;height:${l.height}px;transform:scale(${d});transform-origin:0 0;overflow:hidden`,s.append(this.el),this.doc=e;let u=new Map,p=b=>{for(let M of b)u.set(M.id,M.frame),M.children&&p(M.children)};p(e.elements);let m=dn(e.animation?.clips??[],u);this.motions=m.motions;let y=new Set(m.unplayed.map(b=>`preset ${b}`)),A=[this.loadFonts()],f=i.media?tn(e,void 0,mn(e,n),i.media.capability):[];this.native=new Set(f.filter(b=>b.path.path==="native").map(b=>b.element));let k=(b,M)=>{for(let w of b){if(w.hidden)continue;let T=this.element(w,y,A);M.append(T),w.children?.length&&w.type==="shape"&&k(w.children,T)}};k(e.elements,this.el),e.background&&Jt(this.el,e.background,l,f.filter(b=>this.native.has(b.element)).map(b=>b.box)),this.unplayed=[...y].sort(),this.ready=Promise.all(A).then(()=>{})}path(e){return this.o.path?`${this.o.path}/${e}`:e}element(e,n,s){let i=Yt(e);Xt(i,e,this.source);let o=this.motions.get(e.id);if(o){let r={el:i,e,motion:o,text:e.type==="text"?i.textContent??"":void 0};this.animated.push(r),this.place(r,0)}if(e.type==="library"&&e.source){let r=e.source.kind==="playlist"?this.source.playlist(e.source.item)??[]:[{item:e.source.item}],a=new nt(i,r,e.playback??{},this.source,this.path(e.id),{...this.o,media:this.native.has(e.id)?this.o.media:void 0},e.source.kind==="playlist"?e.source.item:void 0);this.loops.push(a),s.push(a.ready);for(let l of a.unplayed)n.add(l)}else if(e.type==="app"){let r=e.app?this.source.app?.(e.app):null;r?s.push(this.mountApp(e,i,r)):n.add(`app ${e.app?.id??e.id}`)}return i}place(e,n){cn(e.el,e.e,ln(e.motion,e.e.frame,n,this.reduced),e.text);let s=e.e.type==="text"?e.el.firstElementChild:null;s&&qe(s)}loadFonts(){return en(this.doc.fonts??[],e=>this.source.font(e))}async mountApp(e,n,s){let i=this.path(e.id),o=e.app,r=(e.appInfo??s.info)?.id,a={now:()=>this.heldAt??Date.now(),startedAt:()=>this.startedAt||Date.now()},l=(p,m)=>this.o.onApp?.({type:"app.failed",element:i,item:o,...r?{app:r}:{},reason:p,...m?{error:m}:{}}),d=this.o.isolate?{...this.o.isolate,fonts:this.doc.fonts,font:p=>this.source.font(p)}:void 0,u=await Qt(e,n,s,this.source,a,this.o.appDeadlineMs??5e3,p=>{this.disposed?p.dispose():this.apps.push({app:p,box:n,path:i,id:e.id,item:o})},p=>l("error",p),d);if(!("held"in u)){if("failed"in u){l(u.failed,u.error);return}u.late&&(this.late.push(i),this.o.onAppLate?.(i,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(),i=s-this.startedAt;for(let o of this.animated)this.place(o,i);for(let o of this.apps)o.app.update(s);this.frame=requestAnimationFrame(n)};(this.animated.length||this.apps.length)&&(this.frame=requestAnimationFrame(n)),await Promise.all(this.loops.map(s=>s.start()))}hooks(e){let n=this.doc.animation?.clips??[],s=ae(n).clips,i=typeof matchMedia=="function"&&matchMedia("(prefers-reduced-motion: reduce)").matches;for(let o of this.apps){let a=n.map((f,k)=>({c:f,t:s[k]})).filter(f=>f.c.element===o.id&&(f.c.kind==="enter"||f.c.kind==="exit")).filter(f=>o.app.has(f.c.kind));if(!a.length)continue;let l=o.box,d=this.animated.findIndex(f=>f.el===l);d>=0&&(this.animated.splice(d,1),Object.assign(l.style,{opacity:"",transform:"",clipPath:""}));let u=(f,k,b)=>this.hook(o,f.kind,{startAt:k,preset:f.preset??"fade",durationMs:f.durationMs,easing:f.easing,delayMs:0,...f.staggerMs?{staggerMs:f.staggerMs}:{},...b!==void 0?{page:b}:{},reducedMotion:i},l);for(let{c:f,t:k}of a)u(f,e+k.startMs);let p=o.app.paging();if(!p)continue;let m=a.find(f=>f.c.kind==="enter")?.c,y=a.find(f=>f.c.kind==="exit")?.c,A=pn(p.count,p.ms,Ct(this.doc,this.source).durationMs);for(let f=1;f<A.length;f++){let k=e+A[f].startMs;y&&u(y,k-y.durationMs,A[f-1].page),m&&u(m,k,A[f].page)}}}hook(e,n,s,i){let o=!1;e.app.hook(n,s).finally(()=>{o=!0});let r=s.startAt+s.durationMs;this.timers.push(setTimeout(()=>{o||!this.running||(this.overran.includes(e.path)||(this.overran.push(e.path),this.o.onAppOverran?.(e.path,e.item.id),this.o.onApp?.({type:"app.failed",element:e.path,item:e.item,reason:"overran"})),n==="exit"&&(i.style.visibility="hidden"))},Math.max(0,r+fi-Date.now())))}pause(){this.heldAt=Date.now();for(let e of this.loops)e.pause();for(let e of this.apps)e.app.update(this.heldAt)}seek(e){this.running=!1,cancelAnimationFrame(this.frame);for(let n of this.loops)n.pause();for(let n of this.animated)this.place(n,e);this.heldAt=this.startedAt+e;for(let n of this.apps)n.app.update(this.heldAt)}resume(){this.heldAt=void 0;for(let e of this.loops)e.resume()}paging(e){return this.apps.find(n=>n.id===e)?.app.paging()??null}dispose(){this.running=!1,this.disposed=!0,cancelAnimationFrame(this.frame);for(let e of this.timers)clearTimeout(e);for(let e of this.loops)e.stop();for(let e of this.apps)e.app.dispose();this.el.remove()}},hn=t=>Math.min(Math.max(0,t),2**31-1),nt=class{constructor(e,n,s,i,o,r,a){h(this,"region",e);h(this,"playback",s);h(this,"source",i);h(this,"path",o);h(this,"o",r);h(this,"list",a);h(this,"ready");h(this,"unplayed",[]);h(this,"renderer");h(this,"slides");h(this,"order",[]);h(this,"pass",0);h(this,"at",0);h(this,"timer");h(this,"current");h(this,"shownAt",0);h(this,"remaining");h(this,"stopped",!1);h(this,"ended",!1);h(this,"empty",!1);h(this,"open");h(this,"pages");this.renderer=new xe(e,{media:r.media,fit:s.fit,path:o,onItem:r.onItem,onPassEnd:r.onPassEnd,onApp:r.onApp}),s.transition==="slide"&&this.unplayed.push("transition slide");let l=s.transition==="fade"?{type:"crossfade"}:void 0,d=n.map(u=>i.slide(u,{imageDurationMs:s.imageDurationMs,transition:l,region:!0})).filter(u=>!!u);this.slides=d.length===1&&d[0].untilEnd&&(s.onEnd??"loop")==="loop"?[{...d[0],untilEnd:!1,loop:!0}]:d,this.renderer.onMediaEnd(u=>{u===this.current&&!this.stopped&&(clearTimeout(this.timer),this.next("completed"))}),this.ready=this.newPass().then(()=>this.slides.length?this.renderer.prepare(this.slides[this.order[0]]):void 0).catch(()=>{})}async newPass(){let e=this.slides.length;this.order=this.playback.order==="shuffle"?await un(this.source.deviceId,this.path,this.pass,e):[...Array(e).keys()],this.at=0}async start(){this.slides.length&&await this.first()}bounds(e){return[this.source.bounds?.(e.item),this.list&&this.source.bounds?.(this.list)]}seek(e){let n=Date.now(),s=e;for(;s<this.order.length&&!this.bounds(this.slides[this.order[s]]).every(i=>Lt(i,n));)s++;return s}async first(){if(this.at=this.seek(0),this.at<this.order.length){this.empty=!1,await this.show();return}this.empty=!0,this.current=void 0,this.renderer.standby();let e=Date.now(),n=this.slides.flatMap(s=>this.bounds(s).map(i=>i?.from??0)).filter(s=>s>e);clearTimeout(this.timer),n.length&&(this.timer=setTimeout(()=>{!this.stopped&&!this.ended&&this.first()},hn(Math.min(...n)-e)))}async show(){let e=this.slides[this.order[this.at]];this.current=e;try{await this.renderer.show(e)}catch(s){if(this.stopped)return;this.o.onItem?.({type:"item.error",element:this.path,item:e.item,blob:e.blob.hash,...Ye(s)}),this.o.onItem?.({type:"item.end",element:this.path,item:e.item,blob:e.blob.hash,endedAt:Date.now(),reason:"error"}),this.schedule(gi);return}if(this.stopped)return;if(this.shownAt=Date.now(),this.open=e,this.o.onItem?.({type:"item.start",element:this.path,item:e.item,blob:e.blob.hash,contentType:e.blob.contentType,startedAt:this.shownAt,durationMs:e.durationMs}),e.pages&&(this.pages=new ie(this.renderer,e,this.shownAt,!1)),!(this.slides.length===1&&e.loop))this.schedule(e.durationMs+(e.untilEnd?5e3:0));else{let s=this.bounds(e).flatMap(i=>i?.until??[]);s.length&&this.schedule(Math.min(...s)-this.shownAt)}let n=this.peek();n&&n!==e&&this.renderer.prepare(n).catch(()=>{})}peek(){let e=this.seek(this.at+1);if(e<this.order.length)return this.slides[this.order[e]];if(this.o.endsWith!==this.path)return this.playback.onEnd==="hold"||this.playback.onEnd==="hide"||this.playback.order==="shuffle"?void 0:this.slides[this.order[0]]}schedule(e){clearTimeout(this.timer),this.timer=setTimeout(()=>{this.next("completed")},hn(e))}async next(e){if(this.stopped||this.ended||!this.current)return;let n=()=>this.endOpen(e),s=this.seek(this.at+1);if(s<this.order.length){this.at=s,n(),await this.show();return}let o=this.o.onPassEnd?.(this.path)==="hold"?"hold":this.playback.onEnd??"loop";if(o==="hold"){this.ended=!0;return}if(o==="hide"){this.ended=!0,n(),this.renderer.standby(),this.region.style.visibility="hidden";return}this.pass++,await this.newPass(),n(),await this.first()}pause(){clearTimeout(this.timer),this.current&&this.remaining===void 0&&(this.remaining=Math.max(0,this.current.durationMs-(Date.now()-this.shownAt))),this.pages?.pause(),this.renderer.pause()}resume(){if(this.empty&&!this.ended&&!this.stopped){this.first();return}this.remaining===void 0||this.ended||(this.renderer.resume(),this.pages?.resume(),this.shownAt=Date.now()-(this.current.durationMs-this.remaining),this.schedule(this.remaining),this.remaining=void 0)}endOpen(e){this.pages?.stop(),this.pages=void 0;let n=this.open;n&&(this.open=void 0,this.o.onItem?.({type:"item.end",element:this.path,item:n.item,blob:n.blob.hash,endedAt:Date.now(),reason:e}))}stop(){this.endOpen("stopped"),this.stopped=!0,clearTimeout(this.timer),this.renderer.standby();for(let e of this.slides)this.renderer.release(e)}};var st=t=>t!=null&&t!==""&&!(Array.isArray(t)&&t.length===0);function gn(t,e,n,s=[]){let i=t[n];if(!i)return!1;if(!i.when)return!0;if(s.includes(n))return!1;let o=r=>String(r);for(let[r,a]of Object.entries(i.when)){if(!t[r]||r===n)return!1;let d=gn(t,e,r,[...s,n])?e[r]!==void 0?e[r]:t[r].default:void 0,u=Array.isArray(d)?d.map(o):st(d)?[o(d)]:[];if(a.set!==void 0&&a.set!==st(d)||a.anyOf&&!(st(d)&&!Array.isArray(d)?a.anyOf.map(o).includes(o(d)):u.some(p=>a.anyOf.map(o).includes(p)))||a.allOf&&!a.allOf.map(o).every(p=>u.includes(p))||a.noneOf&&u.some(p=>a.noneOf.map(o).includes(p)))return!1}return!0}function yn(t,e){return Object.keys(t).filter(n=>gn(t,e,n))}var ca=re.apiVersion,bn=new Map(re.commands.map(t=>[t.name,t]));var z=class extends Error{constructor(n,s){super(s);h(this,"code",n)}};function wn(t,e){let n=e.capability??"read",s={kind:"app",app:e.id,instance:e.instance},i=new Set,o=!1,r=async(a,l={})=>{if(o)throw new z("NOT_ALLOWED","This app has been removed from the screen.");let d=bn.get(a);if(!d)throw new z("UNKNOWN",`There is no command ${a}.`);if(d.apps==="none")throw new z("NOT_ALLOWED",`Apps cannot call ${a}.`);if(d.apps==="control"&&n!=="control")throw new z("NOT_ALLOWED",`${a} needs capabilities.player: control in ${e.id}'s definition.`);let u=await t.call(a,l,s);if(u.ok)return u.result;if(u.code==="PREVIEW")return{applied:!1,reason:"preview"};throw new z(u.code,u.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 d=t.on(a,l),u=()=>{d(),i.delete(u)};return i.add(u),u},time:()=>r("time.now"),call:r,play:()=>r("player.play"),pause:()=>r("player.pause"),stop:()=>r("player.stop"),next:()=>r("player.next"),previous:()=>r("player.previous"),finish:()=>r("app.finish"),dispose(){o=!0;for(let a of[...i])a()}}}var vn=["none","fade","scale","slide-up","slide-left"],Se=[["1920 \xD7 1080",1920,1080],["1080 \xD7 1920",1080,1920],["3840 \xD7 2160",3840,2160]],yi=[.25,.5,1,2,4],pe=3e4,En=5e3,bi={id:"med_preview",version:1},c={width:1920,height:1080,props:{},touched:new Set,playing:!0,speed:1,t:0,enter:"none",exit:"none",clipMs:800,timeZone:Intl.DateTimeFormat().resolvedOptions().timeZone,locale:navigator.language||"en-US",screen:{name:"Preview screen",lat:49.9,lon:-97.14,fields:"{}"},samples:{},tables:new Map,people:[]},I={frames:"",tables:{},controller:{status:{},events:{}}},U={people:new Set,player:new Map},Ce=[],rt=new Map,S=null,ot=null,at=null,Sn=-1,V=null,le=0,X={wall:0,t:0},Te=0,Pe=[],$=t=>document.getElementById(t),kn=$("stage"),wi=$("controls"),xn=$("report");function g(t,e={},...n){let s=document.createElement(t);for(let[i,o]of Object.entries(e))o==null||o===!1||(i.startsWith("on")&&typeof o=="function"?s.addEventListener(i.slice(2),o):i==="value"||i in s&&typeof o!="string"?s[i]=o:s.setAttribute(i,o===!0?"":String(o)));for(let i of n)i!=null&&s.append(i);return s}var vi=0;function E(t,e,n=!1){return e.id||(e.id=`c${++vi}`),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 B(t,e,n){let s=g("select",{onchange:()=>n(s.value)},...t.map(i=>{let[o,r]=Array.isArray(i)?i:[i,i];return g("option",{value:o,selected:o===e},r)}));return s}async function An(){let[t,e,n,s]=await Promise.all([fetch("/app.json").then(r=>r.json()),fetch("/app.js"),fetch("/samples.json").then(r=>r.json()),fetch("/config.json").then(r=>r.json())]);I=s;for(let[r,a]of Object.entries(I.tables))c.tables.has(dt(r))||Tn(r,a);S=t,Sn=Number(e.headers.get("x-preview-version")??-1),at=e.ok?null:await e.text(),ot=e.ok?new Uint8Array(await e.arrayBuffer()):null;let i=S.params??{},o={};for(let[r,a]of Object.entries(i))o[r]=c.touched.has(r)?c.props[r]:a.default;c.props=o;for(let[r,a]of Object.entries(n))r in c.samples||(c.samples[r]=a);Pn(),document.title=`${S.name}: Wyndlass app preview`,$("title").textContent=S.name,$("subtitle").textContent=`${S.id} ${S.version}`}var dt=t=>`tbl_${(t.replace(/\.(json|csv)$/i,"").toLowerCase().replace(/[^a-z0-9]+/g,"")||"table").slice(0,26).padEnd(26,"0")}`;function ki(t){let e=[],n=[],s="",i=!1;for(let o=0;o<t.length;o++){let r=t[o];if(i){r==='"'&&t[o+1]==='"'?(s+='"',o++):r==='"'?i=!1:s+=r;continue}r==='"'?i=!0:r===","?(n.push(s),s=""):r===`
|
|
5
|
-
`||
|
|
6
|
-
`&&o++,n.push(s),e.push(n),n=[],s=""):s+=
|
|
7
|
-
${
|
|
4
|
+
${n}`))).slice(0,8),16),o=xr(r),i=Array.from({length:s},(a,p)=>p);for(let a=s-1;a>0;a--){let p=Math.floor(o()*(a+1));[i[a],i[p]]=[i[p],i[a]]}return i}var Mr=250,Ar=1e3;function Dt(t,e){let n=t.animation?.duration;if(n?.kind==="fixed")return{durationMs:n.ms};if(n?.kind==="untilPlaylistEnds"){let s=vn(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:de(t.animation?.clips??[]).autoDurationMs}}function wn(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(p=>{let l=e.slide(p);if(!l)return!1;if(l.blob.contentType.startsWith("video/"))return!0;let c=l.layout;return!!c&&n<4&&Object.values(wn(c.doc,c.source,n+1)).some(d=>d.video)})}}i.children&&r(i.children)}};return r(t.elements),s}function vn(t,e){for(let n of t){if(n.id===e)return n;let s=n.children&&vn(n.children,e);if(s)return s}}var Y=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,"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,p=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 l=Math.min(o/p.width,i/p.height);this.el=j("div"),this.el.dataset.layout=p.id,this.el.style.cssText=`position:absolute;left:${(o-p.width*l)/2}px;top:${(i-p.height*l)/2}px;width:${p.width}px;height:${p.height}px;transform:scale(${l});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=mn(e.animation?.clips??[],c);this.motions=m.motions;let g=new Set(m.unplayed.map(v=>`preset ${v}`)),w=[this.loadFonts()],f=r.media?an(e,void 0,wn(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 P=this.element(k,g,w);A.append(P),k.children?.length&&k.type==="shape"&&b(k.children,P)}};b(e.elements,this.el),e.background&&en(this.el,e.background,p,f.filter(v=>this.native.has(v.element)).map(v=>v.box)),this.unplayed=[...g].sort(),this.ready=Promise.all(w).then(()=>{})}path(e){return this.o.path?`${this.o.path}/${e}`:e}element(e,n,s){let r=tn(e);nn(r,e,this.source);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 st(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 p of a.unplayed)n.add(p)}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){gn(e.el,e.e,hn(e.motion,e.e.frame,n,this.reduced),e.text);let s=e.e.type==="text"?e.el.firstElementChild:null;s&&_e(s)}loadFonts(){return on(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()},p=(d,m)=>this.o.onApp?.({type:"app.failed",element:r,item:o,...i?{app:i}:{},reason:d,...m?{error:m}:{}}),l=this.o.isolate?{...this.o.isolate,fonts:this.doc.fonts,font:d=>this.source.font(d)}:void 0,c=await rn(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=>p("error",d),l);if(!("held"in c)){if("failed"in c){p(c.failed,c.error);return}c.late&&(this.late.push(r),this.o.onAppLate?.(r,o.id),p("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=de(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 p=o.box,l=this.animated.findIndex(f=>f.el===p);l>=0&&(this.animated.splice(l,1),Object.assign(p.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},p);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,g=a.find(f=>f.c.kind==="exit")?.c,w=fn(d.count,d.ms,Dt(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+Mr-Date.now())))}pause(){this.heldAt=Date.now();for(let e of this.loops)e.pause();for(let e of this.apps)e.app.update(this.heldAt)}seek(e){this.running=!1,cancelAnimationFrame(this.frame);for(let n of this.loops)n.pause();for(let n of this.animated)this.place(n,e);this.heldAt=this.startedAt+e;for(let n of this.apps)n.app.update(this.heldAt)}resume(){this.heldAt=void 0;for(let e of this.loops)e.resume()}paging(e){return this.apps.find(n=>n.id===e)?.app.paging()??null}dispose(){this.running=!1,this.disposed=!0,cancelAnimationFrame(this.frame);for(let e of this.timers)clearTimeout(e);for(let e of this.loops)e.stop();for(let e of this.apps)e.app.dispose();this.el.remove()}},bn=t=>Math.min(Math.max(0,t),2**31-1),st=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 Me(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 p=s.transition==="fade"?{type:"crossfade"}:void 0,l=n.map(c=>r.slide(c,{imageDurationMs:s.imageDurationMs,transition:p,region:!0})).filter(c=>!!c);this.slides=l.length===1&&l[0].untilEnd&&(s.onEnd??"loop")==="loop"?[{...l[0],untilEnd:!1,loop:!0}]:l,this.renderer.onMediaEnd(c=>{c===this.current&&!this.stopped&&(clearTimeout(this.timer),this.next("completed"))}),this.ready=this.newPass().then(()=>this.slides.length?this.renderer.prepare(this.slides[this.order[0]]):void 0).catch(()=>{})}async newPass(){let e=this.slides.length;this.order=this.playback.order==="shuffle"?await yn(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=>Ot(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()},bn(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,...Xe(s)}),this.o.onItem?.({type:"item.end",element:this.path,item:e.item,blob:e.blob.hash,endedAt:Date.now(),reason:"error"}),this.schedule(Ar);return}if(this.stopped)return;if(this.shownAt=Date.now(),this.open=e,this.o.onItem?.({type:"item.start",element:this.path,item:e.item,blob:e.blob.hash,contentType:e.blob.contentType,startedAt:this.shownAt,durationMs:e.durationMs}),e.pages&&(this.pages=new ie(this.renderer,e,this.shownAt,!1)),!(this.slides.length===1&&e.loop))this.schedule(e.durationMs+(e.untilEnd?5e3:0));else{let s=this.bounds(e).flatMap(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")},bn(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 rt=t=>t!=null&&t!==""&&!(Array.isArray(t)&&t.length===0);function kn(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 l=kn(t,e,i,[...s,n])?e[i]!==void 0?e[i]:t[i].default:void 0,c=Array.isArray(l)?l.map(o):rt(l)?[o(l)]:[];if(a.set!==void 0&&a.set!==rt(l)||a.anyOf&&!(rt(l)&&!Array.isArray(l)?a.anyOf.map(o).includes(o(l)):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 xn(t,e){return Object.keys(t).filter(n=>kn(t,e,n))}var ka=oe.apiVersion,Mn=new Map(oe.commands.map(t=>[t.name,t]));var K=class extends Error{constructor(n,s){super(s);h(this,"code",n)}};function An(t,e){let n=e.capability??"read",s={kind:"app",app:e.id,instance:e.instance},r=new Set,o=!1,i=async(a,p={})=>{if(o)throw new K("NOT_ALLOWED","This app has been removed from the screen.");let l=Mn.get(a);if(!l)throw new K("UNKNOWN",`There is no command ${a}.`);if(l.apps==="none")throw new K("NOT_ALLOWED",`Apps cannot call ${a}.`);if(l.apps==="control"&&n!=="control")throw new K("NOT_ALLOWED",`${a} needs capabilities.player: control in ${e.id}'s definition.`);let c=await t.call(a,p,s);if(c.ok)return c.result;if(c.code==="PREVIEW")return{applied:!1,reason:"preview"};throw new K(c.code,c.message)};return{app:e.id,instance:e.instance,capability:n,status:()=>t.status(),get:a=>t.status()[a],on(a,p){if(o)return()=>{};let l=t.on(a,p),c=()=>{l(),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 it={"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 En(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 Sn(t,e){let n={},s=c=>{n[c]=(n[c]??0)+1},r=(c,d)=>new Proxy(c,{construct:(m,g,w)=>(s(d),Reflect.construct(m,g,w)),apply:(m,g,w)=>(s(d),Reflect.apply(m,g,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 g=c&&c[d];typeof g=="function"&&(c[d]=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 p=new WeakSet,l=(c,d,m)=>!m||typeof m!="object"||p.has(m)?null:(p.add(m),"context");return i(t.HTMLCanvasElement?.prototype,"getContext",l),i(t.OffscreenCanvas?.prototype,"getContext",l),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 Tn=["none","fade","scale","slide-up","slide-left"],Te=[["1920 \xD7 1080",1920,1080],["1080 \xD7 1920",1080,1920],["3840 \xD7 2160",3840,2160]],Er=[.25,.5,1,2,4],ce=3e4,Rn=5e3,Sr={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:[]},T={frames:"",tables:{},controller:{status:{},events:{}}},_={people:new Set,player:new Map},Re=[],dt=new Map,S=null,lt=null,pt=null,Nn=-1,q=null,pe=0,G={wall:0,t:0},Pe=0,Le=[],D=t=>document.getElementById(t),ct=D("stage"),Tr=D("controls"),Pn=D("report"),ot=D("made"),Pr=Sn(window,()=>En(ct)),ut={kinds:{},nodes:0};addEventListener("message",t=>{let e=t.data;T.frames&&t.origin==="null"&&e?.wyndlassPreview==="made"&&e.made&&typeof e.made=="object"&&(ut=e.made)});var On=500,Ln=3,$={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 Lr=0;function E(t,e,n=!1){return e.id||(e.id=`c${++Lr}`),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 U(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 In(){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())]);T=s;for(let[i,a]of Object.entries(T.tables))u.tables.has(mt(i))||$n(i,a);S=t,Nn=Number(e.headers.get("x-preview-version")??-1),pt=e.ok?null:await e.text(),lt=e.ok?new Uint8Array(await e.arrayBuffer()):null;let r=S.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);Dn(),document.title=`${S.name}: Wyndlass app preview`,D("title").textContent=S.name,D("subtitle").textContent=`${S.id} ${S.version}`}var mt=t=>`tbl_${(t.replace(/\.(json|csv)$/i,"").toLowerCase().replace(/[^a-z0-9]+/g,"")||"table").slice(0,26).padEnd(26,"0")}`;function Ir(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
|
+
`||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 Cr(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 Rr(t,e){let n=mt(t),s=(o,i)=>({table:n,version:1,rows:i,columns:o.map(({key:a,label:p})=>({key:a,label:p,type:i.length&&i.every(l=>l[a]===""||l[a]===void 0||Number.isFinite(Number(l[a])))?"number":"text"}))});if(/\.csv$/i.test(t)){let[o=[],...i]=Ir(e),a=new Set,p=o.map(d=>({label:d.trim(),key:Cr(d.trim()||"column",a)})),l=i.map(d=>Object.fromEntries(p.map((m,g)=>[m.key,d[g]??""]))),c=s(p,l);for(let d of c.columns)if(d.type==="number")for(let m of l)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 $n(t,e){try{u.tables.set(mt(t),Rr(t,e)),dt.delete(t)}catch(n){dt.set(t,String(n.message))}}function Dn(){let t=[...u.tables.keys()][0];if(!(!t||!S))for(let[e,n]of Object.entries(S.params??{}))n.type==="datatable"&&!u.props[e]&&(u.props[e]=t,u.touched.add(e))}function Nr(){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 _.people.add(t),()=>{_.people.delete(t)}}}}function at(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 _.people)s([n]);D("people-count").textContent=`${u.people.length} in view`}var Or={status:()=>T.controller.status,on(t,e){let n=_.player.get(t)??new Set;return n.add(e),_.player.set(t,n),()=>{n.delete(e)}},async call(t,e,n){return Re.push(`controller: ${n.app??"the app"} called ${t}, not applied (a preview)`),X({}),{ok:!1,code:"PREVIEW",message:"a preview"}}},$r={time:"time.now",play:"player.play",pause:"player.pause",stop:"player.stop",next:"player.next",previous:"player.previous",finish:"app.finish"};function Dr(t){let e=s=>r=>{throw Re.push(`controller: ${s} refused, ${r.code??"error"}: ${r.message}`),X({}),r},n={...t,call:(s,r)=>t.call(s,r).catch(e(s))};for(let[s,r]of Object.entries($r))n[s]=()=>t[s]().catch(e(r));return n}function jr(t){for(let e of[t.type,"*"])for(let n of _.player.get(e)??[])n(t)}function Fr(){return new Ee({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 Hr(){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:ce-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:Sr,props:u.props,...S.slots&&u.tables.size?{binding:{table:[...u.tables.keys()][0]}}:{},...S.capabilities?.sensors?{sensors:S.capabilities.sensors}:{},appInfo:{id:S.id,element:S.element,player:S.capabilities?.player==="control"?"control":"read"}}],animation:{duration:{kind:"fixed",ms:ce},clips:t}}}async function Ie(){let t=++pe;if(cancelAnimationFrame(Pe),q?.dispose(),q=null,ct.replaceChildren(),Le.length=0,Re.length=0,$.last=null,$.reads=[],ut={kinds:{},nodes:0},!S||!lt){X({building:!0});return}let e={};try{e=JSON.parse(u.screen.fields||"{}")}catch{}let n=lt,s={deviceId:"preview",slide:()=>null,playlist:()=>null,font:()=>{},app:()=>({hash:`preview-${pe}`,bytes:n}),data:Fr(),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,...T.frames?{}:{...S.capabilities?.sensors?.includes("impressions")?{impressions:Nr()}:{},controllerFor:a=>Dr(An(Or,a))}};_.people.clear(),_.player.clear();let r=performance.now(),o=!1,i=new Y(Hr(),s,ct,{appDeadlineMs:Rn,...T.frames?{isolate:{frames:T.frames,apps:()=>!0}}:{},onAppLate:()=>{o=!0},onApp:a=>{t===pe&&(Le.push(`app.failed: ${a.reason}${a.error?` (${a.error})`:""}`),X({}))}});q=i,X({waiting:!0}),await i.ready,t===pe&&(X({readyMs:o?void 0:Math.round(performance.now()-r),late:o}),await i.start(Date.now()-u.t),t===pe&&(i.seek(u.t),G={wall:Date.now(),t:u.t},Br(),Ur()))}function Br(){cancelAnimationFrame(Pe);let t=()=>{if(q){if(u.playing){if(u.t=G.t+(Date.now()-G.wall)*u.speed,u.t>=ce){u.t=0,Ie();return}q.seek(u.t)}D("clock-seek").value=String(Math.round(u.t)),D("clock-time").textContent=`${(u.t/1e3).toFixed(1)} s`,Pe=requestAnimationFrame(t)}};Pe=requestAnimationFrame(t)}function Ur(){let t=D("pages"),e=q?.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:()=>jn(s*e.ms)},`Page ${s+1}`)))):y("p",{"data-testid":"pages"},"One page"))}function jn(t){u.t=Math.max(0,Math.min(ce-1,t)),G={wall:Date.now(),t:u.t},q?.seek(u.t)}function X(t){let e=[];if(pt?e.push(y("pre",{"data-testid":"build-error"},`The module does not build:
|
|
7
|
+
${pt}`)):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 ${Rn/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=Pn.querySelector('[data-testid="ready"]');n&&e.push(n)}for(let n of Le)e.push(y("span",{class:"failed","data-testid":"failure"},n));T.frames&&Le.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 Re.slice(-5))e.push(y("span",{"data-testid":"command"},n));Pn.replaceChildren(...e)}function qr(){let t=T.frames?ut:Pr();if($.last&&u.playing&&q){let i={};for(let[a,p]of Object.entries(t.kinds)){let l=p-($.last.kinds[a]??0);l>0&&(i[a]=l)}$.reads=[...$.reads,i].slice(-Ln)}$.last=t;let e=$.reads.length===Ln?Object.keys(it).filter(i=>$.reads.every(a=>a[i])):[],n=i=>Math.round($.reads.reduce((a,p)=>a+(p[i]??0),0)*1e3/(On*$.reads.length)),s=e.map(i=>[i,`Makes ${n(i)} ${it[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===$.said&&ot.firstChild){ot.querySelector('[data-testid="made-counts"]').textContent=r;return}$.said=o,ot.replaceChildren(...s.map(([i,a])=>y("span",{class:"warn","data-testid":"made","data-kind":i},a)),y("span",{"data-testid":"made-counts"},r))}function _r(){let t=Object.entries(S?.params??{}).find(([,n])=>n.type==="datatable")?.[0],e=S?.slots?[...u.tables.keys()][0]:t?u.props[t]:void 0;return typeof e=="string"?u.tables.get(e):void 0}var Fn=t=>t.label&&t.label!==t.key?`${t.label} (${t.key})`:t.key;function Wr(t,e){let n=u.props[t],s=o=>{u.props[t]=o,u.touched.add(t),Hn(),I()},r=e.label??t;if(e.type==="boolean")return E(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 E(r,U(o,String(n??""),s))}if(e.type==="number")return E(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 E(r,o)}return e.type==="datatable"?E(r,U(["",...u.tables.keys()],String(n??""),s)):e.type==="column"?E(r,U(["",...(_r()?.columns??[]).map(o=>[o.key,Fn(o)])],String(n??""),s)):E(`${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 Hn(){let t=D("params"),e=S?.params??{},n=new Set(xn(e,u.props));t.replaceChildren(...Object.entries(e).filter(([s])=>n.has(s)).map(([s,r])=>Wr(s,r))),t.children.length||t.append(y("p",{},"No params."))}var Cn;function I(){clearTimeout(Cn),Cn=setTimeout(()=>{Ie()},150)}function Ce(){let t=!Te.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,I()}}),n=y("input",{type:"number",value:String(u.height),min:64,max:7680,onchange:i=>{u.height=Number(i.target.value)||1080,I()}}),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)),G={wall:Date.now(),t:u.t}}},u.playing?"Pause":"Play"),r=y("input",{id:"clock-seek",type:"range",min:0,max:ce,step:100,value:String(u.t),oninput:i=>jn(Number(i.target.value))}),o=[y("h2",{},"Screen"),E("Size",U([...Te.map(([i])=>i),"Custom"],t?"Custom":Te.find(([,i,a])=>i===u.width&&a===u.height)[0],i=>{let a=Te.find(([p])=>p===i);a&&(u.width=a[1],u.height=a[2]),Ce(),I()})),t?y("div",{class:"row"},E("Width",e),E("Height",n)):null,y("div",{class:"row"},E("Time zone",y("input",{type:"text",value:u.timeZone,onchange:i=>{u.timeZone=i.target.value||"UTC",I()}})),E("Locale",y("input",{type:"text",value:u.locale,onchange:i=>{u.locale=i.target.value||"en-US",I()}}))),E("Screen's name",y("input",{type:"text",value:u.screen.name,onchange:i=>{u.screen.name=i.target.value,I()}})),y("div",{class:"row"},E("Latitude",y("input",{type:"number",step:"any",value:String(u.screen.lat),onchange:i=>{u.screen.lat=Number(i.target.value),I()}})),E("Longitude",y("input",{type:"number",step:"any",value:String(u.screen.lon),onchange:i=>{u.screen.lon=Number(i.target.value),I()}}))),E("Screen's fields (JSON)",y("textarea",{value:u.screen.fields,onchange:i=>{u.screen.fields=i.target.value,I()}})),y("h2",{},"Clock"),y("div",{class:"row"},s,E("Speed",U(Er.map(i=>[String(i),`${i}\xD7`]),String(u.speed),i=>{G={wall:Date.now(),t:u.t},u.speed=Number(i)})),y("span",{id:"clock-time"},"0.0 s")),E("Time in the layout",r),y("h2",{},"Enter and exit"),y("div",{class:"row"},E("Enter",U(Tn,u.enter,i=>{u.enter=i,I()})),E("Exit",U(Tn,u.exit,i=>{u.exit=i,I()}))),E("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,I()}})),y("div",{id:"pages"}),y("h2",{},"Params"),y("div",{id:"params"}),T.frames?y("p",{"data-testid":"framed"},`In a frame on ${T.frames}, as in the CMS: no storage, cookies, parent page, controller or impressions.`):null,y("h2",{},"Tables"),...[...u.tables.values()].map(i=>y("p",{"data-testid":"table"},`${i.table}: ${i.rows.length} rows, ${i.columns.map(Fn).join(", ")}`)),...[...dt].map(([i,a])=>y("p",{class:"failed","data-testid":"table-error"},`${i}: ${a}`)),E("Add a table (JSON or CSV)",y("input",{type:"file",accept:".json,.csv",onchange:async i=>{let a=i.target.files?.[0];a&&($n(a.name,await a.text()),Dn(),Ce(),I())}})),...S?.capabilities?.sensors?.includes("impressions")&&!T.frames?[y("h2",{},"People at the screen"),y("div",{class:"row"},y("button",{type:"button",onclick:()=>at("enter")},"Someone arrives"),y("button",{type:"button",onclick:()=>at("look")},"Someone looks"),y("button",{type:"button",onclick:()=>at("leave")},"Someone leaves")),y("p",{id:"people-count"},`${u.people.length} in view`)]:[],...T.frames?[]:[y("h2",{},"The player's controller"),y("p",{},`The app may ${S?.capabilities?.player==="control"?"read it and call its commands":"read it (capabilities.player: read)"}. Commands are not applied here.`),...Object.keys(T.controller.events).length?[E("Event",U(Object.keys(T.controller.events),Object.keys(T.controller.events)[0],i=>{D("event-json").value=JSON.stringify(T.controller.events[i],null,2)})),E("Its JSON",y("textarea",{id:"event-json",rows:6,value:JSON.stringify(Object.values(T.controller.events)[0],null,2)})),y("button",{type:"button",onclick:()=>{try{jr(JSON.parse(D("event-json").value))}catch{}}},"Send to the app")]:[]],y("h2",{},"Data"),...Object.keys(u.samples).map(i=>E(`${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),I()}catch{}}}))),Object.keys(u.samples).length?null:y("p",{},"The app declares no data source with a sample (weather, news or calendar).")];Tr.replaceChildren(...o.filter(i=>i!==null)),Hn()}async function zr(){await In(),Ce(),setInterval(qr,On),await Ie();let t=new EventSource("/events"),e=()=>{In().then(()=>(Ce(),Ie()))};t.addEventListener("hello",n=>{JSON.parse(n.data).version!==Nn&&e()}),t.addEventListener("reload",e),window.addEventListener("resize",()=>I())}zr();})();
|
package/dist/react.d.ts
CHANGED
|
@@ -32,11 +32,17 @@ export declare function useApp(): {
|
|
|
32
32
|
screen: ScreenFacts | null;
|
|
33
33
|
preview: boolean;
|
|
34
34
|
};
|
|
35
|
-
/**
|
|
36
|
-
|
|
35
|
+
/**
|
|
36
|
+
* The layout's clock: seeking and pausing show the right moment. Without `everyMs`, the component renders again at each
|
|
37
|
+
* of the layout's frames, many times a second. With it, the time is a multiple of `everyMs` (`useNow(1000)`: whole
|
|
38
|
+
* seconds), and the component renders only when that changes, as a seek to another moment does (WP-0147 decision 2).
|
|
39
|
+
* Take the coarsest the app shows. Either way, make formatters and other objects with `useMemo`, never in the render: one
|
|
40
|
+
* made per frame holds memory a screen does not free in time.
|
|
41
|
+
*/
|
|
42
|
+
export declare function useNow(everyMs?: number): number;
|
|
37
43
|
/**
|
|
38
44
|
* Which of `count` pages shows now, each `ms` long from the layout's start, and the pages said to the host, so a layout
|
|
39
|
-
* can turn its enter and exit with them (ADR 0071, decision 5).
|
|
45
|
+
* can turn its enter and exit with them (ADR 0071, decision 5). The component renders when the page turns (WP-0147).
|
|
40
46
|
*/
|
|
41
47
|
export declare function usePaging(count: number, ms: number): number;
|
|
42
48
|
/**
|
package/dist/react.js
CHANGED
|
@@ -17,7 +17,7 @@ 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, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
20
|
+
import { createContext, createElement, useCallback, useContext, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
21
21
|
import { createRoot } from "react-dom/client";
|
|
22
22
|
import { contextOf, data, kept, snapshot, WyndlassApp } from "./index.js";
|
|
23
23
|
const bridges = new WeakMap();
|
|
@@ -104,20 +104,28 @@ export function useApp() {
|
|
|
104
104
|
};
|
|
105
105
|
}, [app, bridge]);
|
|
106
106
|
}
|
|
107
|
-
/**
|
|
108
|
-
|
|
107
|
+
/**
|
|
108
|
+
* The layout's clock: seeking and pausing show the right moment. Without `everyMs`, the component renders again at each
|
|
109
|
+
* of the layout's frames, many times a second. With it, the time is a multiple of `everyMs` (`useNow(1000)`: whole
|
|
110
|
+
* seconds), and the component renders only when that changes, as a seek to another moment does (WP-0147 decision 2).
|
|
111
|
+
* Take the coarsest the app shows. Either way, make formatters and other objects with `useMemo`, never in the render: one
|
|
112
|
+
* made per frame holds memory a screen does not free in time.
|
|
113
|
+
*/
|
|
114
|
+
export function useNow(everyMs) {
|
|
109
115
|
const [, bridge] = useBridge();
|
|
110
|
-
|
|
116
|
+
const step = everyMs !== undefined && everyMs > 0 ? everyMs : 0;
|
|
117
|
+
const read = useCallback(() => step ? Math.floor(bridge.time() / step) * step : bridge.time(), [bridge, step]);
|
|
118
|
+
return useSyncExternalStore(bridge.subscribe, read, read);
|
|
111
119
|
}
|
|
112
120
|
/**
|
|
113
121
|
* Which of `count` pages shows now, each `ms` long from the layout's start, and the pages said to the host, so a layout
|
|
114
|
-
* can turn its enter and exit with them (ADR 0071, decision 5).
|
|
122
|
+
* can turn its enter and exit with them (ADR 0071, decision 5). The component renders when the page turns (WP-0147).
|
|
115
123
|
*/
|
|
116
124
|
export function usePaging(count, ms) {
|
|
117
125
|
const [, bridge] = useBridge();
|
|
118
|
-
const
|
|
126
|
+
const read = useCallback(() => count > 1 && ms > 0 ? Math.floor(Math.max(0, bridge.time() - bridge.startedAt()) / ms) % count : 0, [bridge, count, ms]);
|
|
119
127
|
useEffect(() => { bridge.setPaging(count > 1 && ms > 0 ? { count, ms } : null); }, [bridge, count, ms]);
|
|
120
|
-
return
|
|
128
|
+
return useSyncExternalStore(bridge.subscribe, read, read);
|
|
121
129
|
}
|
|
122
130
|
/**
|
|
123
131
|
* A data source's answer (one the definition declares): the one the screen kept, at once, then the service's; asked
|
package/package.json
CHANGED
|
@@ -33,6 +33,7 @@ Use the `react` template unless the person asks for plain TypeScript (`vanilla`)
|
|
|
33
33
|
- Run `npm run typecheck` and `npm run check` after each change, and fix what they say.
|
|
34
34
|
- Start `npm run preview` in the background. It serves `http://127.0.0.1:5199/` on this computer only, and draws the app again on every save.
|
|
35
35
|
- **Ask the person to look,** at the screen size they gave and at the other orientation: under **Screen**, set **Size** to 1920 × 1080, then 1080 × 1920. Ask them to play the clock and to pause it. Change what they ask for.
|
|
36
|
+
- **Read what it makes while it plays,** the line below the report. If it says the app makes something at every frame (a formatter, a canvas, an object URL, a listener), fix it as it says before you pack: one made at every frame can run a screen out of memory.
|
|
36
37
|
- **Before importing,** run `npm run preview -- --frame` once and ask them to look again. In the CMS an app runs in a sandboxed frame with no storage, cookies or parent page. An app that fails there works on screens but not in the designer. The frame is served from a second port: use a regular browser for this check, since a browser built into an agent's app may block that port (`net::ERR_BLOCKED_BY_CLIENT`), and the app then reports `app.failed: load`.
|
|
37
38
|
- **Tables in the preview** are keyed as Wyndlass keys a table's columns: from their labels, lowercased, with underscores (a CSV header `Now serving` is the column `now_serving`). A `column` param holds that key, as it will on screens.
|
|
38
39
|
|
|
@@ -88,6 +89,8 @@ Screens or the CMS enforce each of these:
|
|
|
88
89
|
- **Data only from the sources `app.json` declares,** and only those apps may read: `weather`, `news`, `image`, `calendar`.
|
|
89
90
|
- **Network hosts declared truthfully** in `capabilities.network`: a person approves them.
|
|
90
91
|
|
|
92
|
+
One more, which nothing enforces: **make nothing in a draw.** `draw(epochMs)` and a React component run again at every frame, many times a second. Make formatters (`Intl.DateTimeFormat`, `Intl.NumberFormat`) and other objects once: in `mount()`, or with `useMemo` on what they depend on. 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.
|
|
93
|
+
|
|
91
94
|
## When something goes wrong
|
|
92
95
|
|
|
93
96
|
- **A tool you need is missing:** call `whoami`. The import tools are listed only while the organization has imported apps turned on at its top place. An Owner or Admin turns them on in the CMS, under **Settings**, **Imported apps**.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
`class X extends WyndlassApp` (vanilla). Write two methods:
|
|
12
12
|
- `mount()`: build the shadow DOM in `this.root`, once.
|
|
13
|
-
- `draw(epochMs)`: show that moment of the layout's clock. The host calls it on every frame it needs, seeking and pausing included.
|
|
13
|
+
- `draw(epochMs)`: show that moment of the layout's clock. The host calls it on every frame it needs, seeking and pausing included. So it runs many times a second: make formatters and other objects in `mount()`, never here.
|
|
14
14
|
|
|
15
15
|
It gives the element:
|
|
16
16
|
- `prop(name, default)`: a param's value, or the default when it is absent or empty;
|
|
@@ -40,8 +40,8 @@ A paged app also defines `paging()`, returning `{count, ms}`, so the layout can
|
|
|
40
40
|
|
|
41
41
|
`export default reactApp(Component, { css })`: the element's class. The component renders in the app's shadow root, with `css` as its styles. Hooks:
|
|
42
42
|
- `useApp()`: `prop(name, default)`, `ready()`, `timeZone`, `locale`, `place`, `screen`, `preview`;
|
|
43
|
-
- `useNow()`: the layout's clock
|
|
44
|
-
- `usePaging(count, ms)`: the page showing now. It also tells the host the pages;
|
|
43
|
+
- `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
|
+
- `usePaging(count, ms)`: the page showing now, rendering when it turns. It also tells the host the pages;
|
|
45
45
|
- `useData(source, params)`: `{answer, error}`. The kept answer at once, then the service's, asked again every 10 minutes;
|
|
46
46
|
- `useSnapshot(table)`: a data table's rows, as `snapshot` gives them, read again each frame.
|
|
47
47
|
|
|
@@ -28,7 +28,7 @@ Two pages, eight seconds each: a heading and the time, then the weather for the
|
|
|
28
28
|
|
|
29
29
|
1. **Change `app.json` first:** the `params` people set in the designer (each with a `label` and a `default`), the `data` sources it reads, and `capabilities` if it asks for more.
|
|
30
30
|
2. **Read each param** with `prop(name, default)` (vanilla) or `useApp().prop(name, default)` (react), with the same default as `app.json`.
|
|
31
|
-
3. **Draw from the layout's clock:** vanilla's `draw(epochMs)` gets the moment; react's `useNow()` gives it. Work out what changes with time (a page, a countdown, a clock) from that number, never from a timer of your own.
|
|
31
|
+
3. **Draw from the layout's clock:** vanilla's `draw(epochMs)` gets the moment; react's `useNow()` gives it, and `useNow(1000)` gives whole seconds, rendering once a second. Work out what changes with time (a page, a countdown, a clock) from that number, never from a timer of your own. Both run at every frame: make a formatter once, as the templates do (`#format` in `mount()`, `useMemo` in react), never in the draw.
|
|
32
32
|
4. **Say `ready()`** as soon as the app has something to show, within 5 s: the kept answer, a default, or the failure.
|
|
33
33
|
5. **Styles** go in `src/app.css`. Size text to the box with `fit()` or the `--w` and `--h` properties ([contract.md](contract.md)), since screens run from 1080p to 4K.
|
|
34
34
|
6. **Images:** import them from `src/` (`import logo from "./logo.png"`: PNG, JPEG, GIF, WebP or SVG), and `pack` inlines each as a data URL, for an `<img>`'s `src` or a style set from code. A `url()` inside `src/app.css` is not bundled.
|
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
An app for Wyndlass's screens, on `@wyndlass/app-sdk` and React. Its README says what it is; these are the rules.
|
|
4
4
|
|
|
5
5
|
- **The module is `src/app.tsx`;** its default export is `reactApp(Component, { css })`, the element's class. The component renders in the app's shadow root.
|
|
6
|
-
- **Time:** `useNow()` gives the layout's clock, drawn again at each of its frames; never `Date.now()`, so a seek or a pause shows the right moment.
|
|
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
|
+
- **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.
|
|
7
8
|
- **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.
|
|
8
9
|
- **Data:** only through `useData(source, params)`, and only the sources `app.json`'s `data` lists. Pages: `usePaging(count, ms)`.
|
|
9
10
|
- **Styles:** `src/app.css`, passed as `css`, never a `<style>`.
|
|
10
11
|
- **No `localStorage`** for anything that matters, **no secrets** in params or the module, and **network hosts declared** in `capabilities.network`.
|
|
11
|
-
- **Look at it:** `npm run preview` (http://127.0.0.1:5199/), in both orientations, with the clock played and held, and once with `npm run preview -- --frame`, as the CMS runs it.
|
|
12
|
+
- **Look at it:** `npm run preview` (http://127.0.0.1:5199/), in both orientations, with the clock played and held, and once with `npm run preview -- --frame`, as the CMS runs it. Below the report, it says what the app makes at every frame: fix that before packing.
|
|
12
13
|
- **Before importing:** `npm run check`, then `npm run pack`. Raise `version` in `app.json` for each new version.
|
|
13
14
|
- **Importing and scheduling** go through the Wyndlass MCP, with the person's approval: `app.prepareImport`, `npx wyndlass-app upload <url> dist/<zip> --header "name: value"`, `app.import`, then `app.list` until the version is ready or held. A held version waits for a person who may approve media.
|
|
14
15
|
- **Never approve a version:** a person who may approve media does, in the CMS. Give them the link `app.import` answers, and say why it is held.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// __APP_NAME__: a heading and the time on one page, the weather on the next, a React component on Wyndlass's app SDK.
|
|
2
2
|
// Everything it draws comes from the layout's clock (useNow()), so the designer's seek and pause show the right moment.
|
|
3
|
+
// It shows whole seconds at most, so it renders once a second (useNow(1000)), not at every frame.
|
|
3
4
|
import { useEffect, useMemo } from "react";
|
|
4
5
|
import { color, type weather } from "@wyndlass/app-sdk";
|
|
5
6
|
import { reactApp, useApp, useData, useNow, usePaging } from "@wyndlass/app-sdk/react";
|
|
@@ -10,11 +11,12 @@ const PAGE_MS = 8_000;
|
|
|
10
11
|
|
|
11
12
|
function Board() {
|
|
12
13
|
const app = useApp();
|
|
13
|
-
const now = useNow();
|
|
14
|
+
const now = useNow(1000);
|
|
14
15
|
const page = usePaging(2, PAGE_MS);
|
|
15
16
|
// The weather for the screen's place, or Winnipeg's where there is none (the designer's sample).
|
|
16
17
|
const place = app.place ?? { lat: 49.9, lon: -97.14 };
|
|
17
18
|
const { answer, error } = useData<weather.Weather>("weather", { lat: place.lat, lon: place.lon, units: app.prop("units", "metric") });
|
|
19
|
+
// Made once, not in each render: a formatter made in each render holds memory (AGENTS.md).
|
|
18
20
|
const time = useMemo(() => new Intl.DateTimeFormat(app.locale, { timeZone: app.timeZone, hour: "numeric", minute: "2-digit",
|
|
19
21
|
...(app.prop("seconds", false) ? { second: "2-digit" } : {}) }), [app]);
|
|
20
22
|
// Ready once there is something to show: the kept answer at once, else the service's answer or its failure.
|
|
@@ -4,11 +4,12 @@ An app for Wyndlass's screens, on `@wyndlass/app-sdk`. Its README says what it i
|
|
|
4
4
|
|
|
5
5
|
- **The module is `src/app.ts`;** its default export is the element's class, extending `WyndlassApp`. `mount()` builds the shadow DOM once; `draw(epochMs)` draws that moment of the layout's clock.
|
|
6
6
|
- **Time:** from `now()` or `draw`'s `epochMs`, never `Date.now()`, so a seek or a pause shows the right moment.
|
|
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.
|
|
7
8
|
- **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()`.
|
|
8
9
|
- **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)`.
|
|
9
10
|
- **Styles:** `this.css(text)`, never a `<style>`. A CSS file the source imports is its text.
|
|
10
11
|
- **No `localStorage`** for anything that matters, **no secrets** in params or the module, and **network hosts declared** in `capabilities.network`.
|
|
11
|
-
- **Look at it:** `npm run preview` (http://127.0.0.1:5199/), in both orientations, with the clock played and held, and once with `npm run preview -- --frame`, as the CMS runs it.
|
|
12
|
+
- **Look at it:** `npm run preview` (http://127.0.0.1:5199/), in both orientations, with the clock played and held, and once with `npm run preview -- --frame`, as the CMS runs it. Below the report, it says what the app makes at every frame: fix that before packing.
|
|
12
13
|
- **Before importing:** `npm run check`, then `npm run pack`. Raise `version` in `app.json` for each new version.
|
|
13
14
|
- **Importing and scheduling** go through the Wyndlass MCP, with the person's approval: `app.prepareImport`, `npx wyndlass-app upload <url> dist/<zip> --header "name: value"`, `app.import`, then `app.list` until the version is ready or held. A held version waits for a person who may approve media.
|
|
14
15
|
- **Never approve a version:** a person who may approve media does, in the CMS. Give them the link `app.import` answers, and say why it is held.
|