@hopple/sdk 0.1.0-rc.1 → 0.1.0-rc.2
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/config.schema.json +8 -0
- package/hopple-sdk.min.js +30 -14
- package/index.d.ts +57 -43
- package/index.js +57 -20
- package/package.json +1 -1
package/config.schema.json
CHANGED
|
@@ -55,6 +55,14 @@
|
|
|
55
55
|
"maxLength": 64,
|
|
56
56
|
"errorMessage": "must be lowercase letters, digits and hyphens"
|
|
57
57
|
},
|
|
58
|
+
"owner": {
|
|
59
|
+
"type": "string",
|
|
60
|
+
"description": "The handle of the account the game is published under: the first half of `owner/id`. Absent, it is whoever publishes it. Present, only that account and the game's collaborators may publish it.",
|
|
61
|
+
"markdownDescription": "The handle of the account the game is published under: the first half of\n`owner/id`. Absent, it is whoever publishes it. Present, only that account\nand the game's collaborators may publish it.",
|
|
62
|
+
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
|
|
63
|
+
"maxLength": 64,
|
|
64
|
+
"errorMessage": "must be a handle: lowercase letters, digits and hyphens"
|
|
65
|
+
},
|
|
58
66
|
"version": {
|
|
59
67
|
"type": "string",
|
|
60
68
|
"description": "The game's own version, `MAJOR.MINOR.PATCH`. Ours to read, never to parse for meaning.",
|
package/hopple-sdk.min.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
/*! @hopple/sdk 0.1.0-rc.
|
|
2
|
-
"use strict";var Hopple=(()=>{var Se=Object.defineProperty;var Nn=Object.getOwnPropertyDescriptor;var Dn=Object.getOwnPropertyNames;var jn=Object.prototype.hasOwnProperty;var $n=(e,t,n)=>t in e?Se(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Un=(e,t)=>{for(var n in t)Se(e,n,{get:t[n],enumerable:!0})},zn=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Dn(t))!jn.call(e,r)&&r!==n&&Se(e,r,{get:()=>t[r],enumerable:!(o=Nn(t,r))||o.enumerable});return e};var Gn=e=>zn(Se({},"__esModule",{value:!0}),e);var Pt=(e,t,n)=>$n(e,typeof t!="symbol"?t+"":t,n);var Fr={};Un(Fr,{ACHIEVEMENT_VALUE_MAX:()=>Ue,AUDIO_FULL:()=>X,AUDIO_MASTER:()=>V,AUDIO_VOLUME_MAX:()=>ne,BridgeError:()=>R,DEADZONE:()=>Re,INPUT_CONTRACT:()=>ye,INPUT_SCHEMES:()=>xe,INPUT_SHIM:()=>fe,MANIFEST_FILE:()=>wt,MANIFEST_SCHEMA:()=>xt,MAX_STEP:()=>St,PAD_BUTTONS:()=>J,PAGES:()=>ie,PAUSE_NONE:()=>ae,PAUSE_STATES:()=>nt,SAVE_BYTES:()=>Z,SCORE_VALUE_MAX:()=>at,SET_SHIM:()=>U,SHARE_BYTES:()=>lt,SHARE_META_BYTES:()=>pt,SHIM:()=>F,SHIM_WAIT_MS:()=>At,audioVolumeOf:()=>re,findShim:()=>Ce,hostTransport:()=>Oe,init:()=>Pn,insetsFrom:()=>ge,isInputShim:()=>me,isShim:()=>he,labelFor:()=>de,preferredBinding:()=>pe,servesScheme:()=>Le,shimTransport:()=>be});var Mt=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;var Ue=9007199254740991;function ze(e){return typeof e=="string"&&e.length<=48&&Mt.test(e)}function te(e){return typeof e=="number"&&Number.isFinite(e)&&Math.abs(e)<=9007199254740991}function Lt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.unlocked!="boolean")return null;let n={unlocked:t.unlocked};return typeof t.at=="number"&&Number.isFinite(t.at)&&(n.at=t.at),te(t.progress)&&(n.progress=t.progress),n}function ve(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.unlocked!="boolean"||typeof t.earned!="boolean")return null;let n={unlocked:t.unlocked,earned:t.earned};return te(t.progress)&&(n.progress=t.progress),n}function Ge(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,o={ids:Array.isArray(t.ids)?t.ids.filter(ze):[]},r=Rt(t.state);return r!==void 0&&(o.state=r),o}function Be(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=Rt(e.state);return t===void 0?null:{state:t}}function Rt(e){if(e===null)return null;if(typeof e!="object"||e===void 0||Array.isArray(e))return;let t={};for(let[n,o]of Object.entries(e)){if(!ze(n))continue;let r=Lt(o);r!==null&&(t[n]=r)}return t}var Ct="hopple.",He=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function Ot(e){return typeof e=="string"&&!e.startsWith(Ct)&&He.test(e)}function We(e,t,n){let o=typeof e=="string"?e:"";if(!Ot(e))return{ok:!1,reason:"name",name:o};let r=n?.[e];if(n!==void 0&&r===void 0)return{ok:!1,reason:"undeclared",name:e};if(t!==void 0&&!_t(t))return{ok:!1,reason:"props",name:e};let s={},a=[];for(let[u,p]of Object.entries(_t(t)?t:{})){let d=r?.[u];if(r!==void 0&&d===void 0||!He.test(u)||!Bn(p,d)){a.push(u);continue}s[u]=p}return{ok:!0,name:e,props:s,dropped:a}}function _t(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Bn(e,t){return typeof e=="string"?t===void 0||t==="string":typeof e=="number"?Number.isFinite(e)&&(t===void 0||t==="number"):typeof e=="boolean"?t===void 0||t==="boolean":!1}var ne=1,V="master";var Tt=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function Nt(e){return typeof e=="string"&&e.length<=48&&Tt.test(e)}var X=Object.freeze({muted:!1,master:1,channels:Object.freeze({})});function Dt(e,t){return typeof e!="number"||!Number.isFinite(e)?t:Math.min(1,Math.max(0,e))}function Ae(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,n={};if(typeof t.channels=="object"&&t.channels!==null&&!Array.isArray(t.channels))for(let[o,r]of Object.entries(t.channels))!Nt(o)||o===V||typeof r!="number"||!Number.isFinite(r)||(n[o]=Math.min(1,Math.max(0,r)));return{muted:t.muted===!0,master:Dt(t.master,X.master),channels:n}}function Fe(e,t){if(e.muted!==t.muted||e.master!==t.master)return!1;let n=Object.keys(e.channels);return n.length!==Object.keys(t.channels).length?!1:n.every(o=>e.channels[o]===t.channels[o])}function re(e,t){return e.muted?0:t===void 0||t===V?e.master:e.master*(e.channels[t]??1)}var Hn=Object.freeze({audio:!0,loop:!0,resources:!0});function Ve(e){let t=typeof e=="object"&&e!==null?e:{};return{audio:t.audio!==!1,loop:t.loop!==!1,resources:t.resources!==!1}}function oe(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.p!==1)return!1;switch(t.kind){case"req":return typeof t.id=="string"&&typeof t.method=="string";case"res":return typeof t.id!="string"?!1:t.ok===!0?!0:t.ok===!1?Wn(t.error):!1;case"evt":return typeof t.topic=="string";default:return!1}}function Wn(e){if(typeof e!="object"||e===null)return!1;let t=e;return typeof t.code=="string"&&typeof t.message=="string"}var R=class extends Error{constructor(n){super(n.message);Pt(this,"code");this.name="BridgeError",this.code=n.code}};function Xe(e){return e instanceof R?{code:e.code,message:e.message}:{code:"internal",message:"The handler failed."}}function we(e,t){let n,o;try{Fn(t)?(n=t.message===""?String(t.name):`${String(t.name)}: ${t.message}`,o=typeof t.stack=="string"?t.stack:void 0):typeof t=="string"?n=t:typeof t=="object"&&t!==null?n=JSON.stringify(t)??String(t):n=String(t)}catch{n="An error that could not be described."}let r={kind:e,message:jt(n,1e3)};return o!==void 0&&o!==""&&(r.stack=jt(o,8e3)),r}function Fn(e){if(e instanceof Error)return!0;let t=Object.prototype.toString.call(e);return(t==="[object Error]"||t==="[object DOMException]")&&typeof e.message=="string"}function jt(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}var xe=["keyboard","mouse","touch","gamepad"];var Ut=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function q(e){return typeof e=="string"&&e.length<=48&&Ut.test(e)}var zt=["row","column","diamond","cluster"],Gt=["left","right","full"];var Bt=["remap","scheme","host","setting"];function Ke(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(!Array.isArray(t.bind))return null;let o={bind:t.bind.filter(r=>typeof r=="string"&&r.length<=96)};return typeof t.title=="string"&&(o.title=t.title),typeof t.label=="string"&&(o.label=t.label),o}function Ze(e){let t=Wt(e);if(t===null)return null;let n=e,o={};for(let[s,a]of qe(n.actions)){if(!q(s))continue;let u=Ke(a);u!==null&&(o[s]=u)}let r={};for(let[s,a]of qe(n.layouts)){if(!q(s))continue;let u=Ht(a);u!==null&&(r[s]=u)}return{...t,actions:o,layouts:r}}function Ht(e){if(!K(e))return null;let t={};for(let n of Gt){let o=e[n];if(!K(o))continue;let r={};for(let[s,a]of Object.entries(o)){if(!q(s))continue;let u=Vn(a);u!==null&&(r[s]=u)}t[n]=r}return t}function Vn(e){if(!K(e))return null;let t=e.type;if(t!=="buttons"&&t!=="thumbstick")return null;let n={type:t},o=e.buttons;Array.isArray(o)&&(n.buttons=o.filter(q));let r=e.shape;zt.includes(r)&&(n.shape=r);let s=e.as;if($t(s))n.as=s;else if(K(s)){let u={};for(let[p,d]of Object.entries(s))q(p)&&$t(d)&&(u[p]=d);n.as=u}let a=e.axis;return t==="thumbstick"&&(a==="x"||a==="y")&&(n.axis=a),n}function Ye(e){let t=Wt(e);if(t===null)return null;let n=e.cause;return{...t,cause:Bt.includes(n)?n:"host"}}function Wt(e){if(!K(e))return null;let t=e,n={};for(let[o,r]of qe(t.mapping)){if(!q(o))continue;let s=Ke(r);s!==null&&(n[o]=s)}return{mapping:n,scheme:xe.includes(t.scheme)?t.scheme:"keyboard",controls:t.controls===!0,sticks:t.sticks==="fixed"?"fixed":"floating"}}function K(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function qe(e){return K(e)?Object.entries(e):[]}function $t(e){return typeof e=="string"&&e.length<=96}var Je=["app","web","dev"],Qe=["low","mid","high"],B={app:{id:"",version:"",surface:"web"},game:{id:"",version:""},player:{id:null,returning:!1,locale:"en"},device:{tier:"mid"}};function _(e){let t=new Set;function n(o,r){try{return o.listener(...r)}catch(s){e(s);return}}return{on(o){let r={listener:o};return t.add(r),()=>{t.delete(r)}},emit(...o){for(let r of[...t]){if(!t.has(r))continue;let s=n(r,o);s instanceof Promise&&s.catch(e)}},async settle(...o){await Promise.all([...t].map(async r=>{if(t.has(r))try{await n(r,o)}catch(s){e(s)}}))},get size(){return t.size},clear(){t.clear()}}}var Ft=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;var ie=["about","scores","achievements","input"];function se(e){return typeof e=="string"&&e.length<=32&&Ft.test(e)}function et(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,n={ids:Array.isArray(t.ids)?t.ids.filter(se):[]};return(t.isOpen===!0||se(t.page))&&(n.isOpen=!0),se(t.page)&&(n.page=t.page),n}function tt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,n=se(t.page)?t.page:null,o=t.isOpen===!0||n!==null;return{isOpen:o,page:o?n:null}}var nt=["running","paused","unpausable"],ae="unpausable";function rt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;return typeof t.paused=="boolean"?{paused:t.paused}:null}var Z=65536;function st(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.state!="string"||typeof t.version!="number"||!Number.isFinite(t.version)||t.encoding!=="json"&&t.encoding!=="text")return null;let n={version:t.version,encoding:t.encoding,state:t.state};return typeof t.rev=="string"&&(n.rev=t.rev),n}function ke(e){return new TextEncoder().encode(e).length}function Ee(e){if(typeof e!="object"||e===null)return null;let t=e;return!ot(t.at)||!ot(t.version)||!ot(t.bytes)?null:{at:t.at,version:t.version,bytes:t.bytes}}function ot(e){return typeof e=="number"&&Number.isFinite(e)}var at=9007199254740991,Vt=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function it(e){return typeof e=="string"&&e.length<=48&&Vt.test(e)}function ce(e){return typeof e=="number"&&Number.isFinite(e)&&Math.abs(e)<=9007199254740991}function Xt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(!ce(t.best)||!Y(t.at)||!Y(t.runs))return null;let n={best:t.best,at:t.at,runs:t.runs};return Y(t.rank)&&(n.rank=t.rank),Y(t.of)&&(n.of=t.of),n}function Ie(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,o={boards:Array.isArray(t.boards)?t.boards.filter(it):[]};if(t.standing===null)o.standing=null;else if(typeof t.standing=="object"&&t.standing!==void 0){let r={};for(let[s,a]of Object.entries(t.standing)){if(!it(s))continue;let u=Xt(a);u!==null&&(r[s]=u)}o.standing=r}return o}function ct(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(t.standing===null)return{standing:null};let n=Ie({boards:[],standing:t.standing});return n?.standing===void 0?null:{standing:n.standing}}function ut(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.improved!="boolean"||!ce(t.best))return null;let n={improved:t.improved,best:t.best};return Y(t.rank)&&(n.rank=t.rank),Y(t.of)&&(n.of=t.of),n}function Y(e){return typeof e=="number"&&Number.isFinite(e)}var lt=512,pt=1024;function dt(e){if(typeof e!="object"||e===null)return null;let t=e,n={};return typeof t.data=="string"&&t.data!==""&&(n.data=t.data),typeof t.offers=="boolean"&&(n.offers=t.offers),n}function ft(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;try{let t=JSON.parse(JSON.stringify(e));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:null}catch{return null}}var Xn=1e4;function mt(e){let{transport:t}=e,n=e.timeoutMs??Xn,o=e.onError??(g=>console.error(g)),r=e.handlers,s=new Map,a=new Map,u=0,p=null,d=null,c=!1,i=t.receive(m);function m(g){c||(g.kind==="req"?f(g):g.kind==="res"?l(g):b(g))}async function f(g){if(k(g.id)){v({p:1,kind:"res",id:g.id,ok:!1,error:{code:"stale",message:"That conversation has ended; introduce yourself again."}});return}let w=r[g.method];if(!w){v({p:1,kind:"res",id:g.id,ok:!1,error:{code:"unknown_method",message:`No handler for "${g.method}".`}});return}try{let O=await w(g.params);if(c)return;v({p:1,kind:"res",id:g.id,ok:!0,value:O})}catch(O){if(c)return;v({p:1,kind:"res",id:g.id,ok:!1,error:Xe(O)})}}function l(g){let w=s.get(g.id);w&&(s.delete(g.id),clearTimeout(w.timer),g.ok?w.resolve(g.value):w.reject(new R(g.error)))}function b(g){a.get(g.topic)?.emit(g.data)}function v(g){try{t.send(g)}catch(w){o(w)}}function M(g,w,O){if(c)return Promise.reject(new R({code:"disposed",message:`"${g}" was not sent: peer disposed.`}));u+=1;let T=p===null?String(u):`${p}-${u}`,$=O?.timeoutMs??n;return new Promise((C,G)=>{let Te=setTimeout(()=>{s.delete(T),G(new R({code:"timeout",message:`"${g}" got no reply within ${$}ms.`}))},$);s.set(T,{resolve:C,reject:G,timer:Te}),v({p:1,kind:"req",id:T,method:g,params:w})})}function I(g,w){c||v({p:1,kind:"evt",topic:g,data:w})}function A(g,w){let O=a.get(g);O===void 0&&(O=_(o),a.set(g,O));let T=O.on(w),$=O;return()=>{T(),$.size===0&&a.get(g)===$&&a.delete(g)}}function x(g){for(let w of s.values())clearTimeout(w.timer),w.reject(g);s.clear()}function y(g){p=g}function E(g){c||(d=g,x(new R({code:"stale",message:"The other side started over; this call belonged to the conversation before it."})))}function k(g){if(d===null)return!1;let w=g.indexOf("-");return w===-1?!1:g.slice(0,w)!==d}function P(){c||(c=!0,i(),x(new R({code:"disposed",message:"The peer was disposed mid-call."})),a.clear(),t.close())}return{call:M,emit:I,on:A,useSession:y,expectSession:E,dispose:P}}var F="__HOPPLE_SHIM__",U="__HOPPLE_SET_SHIM__";var qt={version:"0.1.0-rc.1"};function Kt(e){return{...e,call:(n,o,r)=>e.call(n,o,r).catch(s=>{throw Kn(s)})}}function W(e,t,n){let o;try{o=e()}catch(r){o=Promise.reject(r)}o.then(n,r=>{r instanceof R&&(r.code==="unsupported"||r.code==="disposed")||t(r)})}function Kn(e){return!(e instanceof R)||e.code!=="unknown_method"?e:new R({code:"unsupported",message:e.message})}function Zt(e){let t=!1,n=[],o,r=yt(n,o),s=_(e.onError);function a(d){o=d;let c=r;r=yt(n,o),Zn(c,r)||s.emit(r,c)}function u(d,c){if(o===void 0)return;let i=o??{},m=i[d],f={unlocked:c.unlocked,...c.unlocked?{at:m?.at??Date.now()}:{},...c.progress===void 0?{}:{progress:c.progress}};a({...i,[d]:f})}return{achievements:{get available(){return t},get standing(){return r},on:s.on,unlock(d){t&&W(()=>e.peer().call("achievement.unlock",{id:d}),e.onError,c=>{u(d,ve(c)??{unlocked:!0,earned:!0})})},progress(d,c){if(!te(c)){e.onError(new Error(`achievements: progress on "${d}" must be a finite number an achievement can hold.`));return}t&&W(()=>e.peer().call("achievement.progress",{id:d,progress:c}),e.onError,i=>{u(d,ve(i)??{unlocked:!1,earned:!1,progress:c})})}},adopt(d){let c=Ge(d);t=c!==null,n=c?.ids??[],o=c?.state,r=yt(n,o)},onChange(d){let c=Be(d);c!==null&&a(c.state)}}}function yt(e,t){if(t===void 0)return Object.freeze({known:!1,items:Object.freeze(e.map(r=>Object.freeze({id:r})))});let n=t??{},o=e.map(r=>Object.freeze({id:r,...n[r]??{unlocked:!1}}));return Object.freeze({known:!0,items:Object.freeze(o)})}function Zn(e,t){return e.known!==t.known||e.items.length!==t.items.length?!1:!e.known||!t.known?!0:e.items.every((n,o)=>{let r=t.items[o];return r!==void 0&&n.id===r.id&&n.unlocked===r.unlocked&&n.at===r.at&&n.progress===r.progress})}function Yt(e){return{refused(t){let n=t,o=typeof n=="object"&&n!==null?n:null;(e.onRefused??e.onError)(new Error(`analytics: the host refused "${String(o?.name)}" (${String(o?.reason)}).`))},analytics:{track(t,n){let o=We(t,n);if(!o.ok){e.onError(new Error(`analytics: "${o.name}" was not sent (${o.reason}).`));return}o.dropped.length>0&&e.onError(new Error(`analytics: "${o.name}" dropped ${o.dropped.join(", ")}.`));let r={name:o.name};Object.keys(o.props).length>0&&(r.props=o.props),e.peer().emit("analytics.event",r)}}}}var Jt=["pointerdown","pointerup","touchend","keydown"];function Qt(e){let t=gt(X),n=!0,o=!1,r=_(e.onError),s=new Map,a=new Map,u=!1;function p(l){return n?re(t,l):0}function d(){for(let l of s.values())for(let[b,v]of l)v.gain.value=p(b);for(let[l,b]of a)l.volume=p(b.channel)}function c(){if(n){for(let l of s.keys())l.state==="suspended"&&l.resume().catch(()=>{});for(let[l,b]of a)b.stopped&&l.play().then(()=>{b.stopped=!1},()=>{})}}async function i(){for(let[l,b]of a)l.paused||(l.pause(),b.stopped=!0);await Promise.all([...s.keys()].map(l=>l.state==="running"?l.suspend().catch(()=>{}):void 0))}function m(){if(!(u||e.scope===null)){u=!0;for(let l of Jt)e.scope.addEventListener(l,c,{capture:!0})}}return{audio:{get allowed(){return n},get muted(){return t.muted},get master(){return t.master},get state(){return t},volume(l){return p(l)},channel(l){return l===V?t.master:t.channels[l]??1},on:r.on,output(l,b){let v=s.get(l);v===void 0&&(v=new Map,s.set(l,v),!n&&l.state==="running"&&l.suspend().catch(()=>{}));let M=v.get(b);return M===void 0&&(M=l.createGain(),M.gain.value=p(b),M.connect(l.destination),v.set(b,M)),m(),c(),M},media(l,b){if(a.set(l,{channel:b,stopped:!1}),l.volume=p(b),!n&&!l.paused){l.pause();let v=a.get(l);v!==void 0&&(v.stopped=!0)}return m(),()=>{a.delete(l)}}},async onAllow(l){return o=!0,l.allowed!==n&&(n=l.allowed,d(),r.emit(),n?c():await i()),{allowed:n}},dispose(){if(u&&e.scope!==null)for(let l of Jt)e.scope.removeEventListener(l,c,{capture:!0});u=!1,s.clear(),a.clear()},adopt(l,b){o||(n=b),t=gt(Ae(l)??X),d()},onSet(l){let b=gt(Ae(l)??t);return Fe(t,b)||(t=b,d(),r.emit()),t}}}function gt(e){return Object.freeze({muted:e.muted,master:e.master,channels:Object.freeze({...e.channels})})}var Yn=20,Jn=1e3,Qn=32;function en(e){let t=null,n=[],o=new Map;function r(c){if(t!==null){t.emit("error.report",c);return}n.length<Yn&&n.push(c)}function s(c,i){let m={error:i,again:0,timer:setTimeout(()=>{o.delete(c),m.again!==0&&(r({...i,count:m.again}),s(c,i))},Jn)};o.set(c,m)}function a(c){let i=`${c.kind}
|
|
1
|
+
/*! @hopple/sdk 0.1.0-rc.2 */
|
|
2
|
+
"use strict";var Hopple=(()=>{var ve=Object.defineProperty;var Nn=Object.getOwnPropertyDescriptor;var Dn=Object.getOwnPropertyNames;var jn=Object.prototype.hasOwnProperty;var $n=(e,t,n)=>t in e?ve(e,t,{enumerable:!0,configurable:!0,writable:!0,value:n}):e[t]=n;var Un=(e,t)=>{for(var n in t)ve(e,n,{get:t[n],enumerable:!0})},zn=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of Dn(t))!jn.call(e,r)&&r!==n&&ve(e,r,{get:()=>t[r],enumerable:!(o=Nn(t,r))||o.enumerable});return e};var Gn=e=>zn(ve({},"__esModule",{value:!0}),e);var Pt=(e,t,n)=>$n(e,typeof t!="symbol"?t+"":t,n);var Fr={};Un(Fr,{ACHIEVEMENT_VALUE_MAX:()=>Ue,AUDIO_FULL:()=>K,AUDIO_MASTER:()=>q,AUDIO_VOLUME_MAX:()=>re,BridgeError:()=>L,DEADZONE:()=>_e,INPUT_CONTRACT:()=>he,INPUT_SCHEMES:()=>ke,INPUT_SHIM:()=>me,MANIFEST_FILE:()=>wt,MANIFEST_SCHEMA:()=>xt,MAX_STEP:()=>St,PAD_BUTTONS:()=>ee,PAGES:()=>ae,PAUSE_NONE:()=>ce,PAUSE_STATES:()=>nt,SAVE_BYTES:()=>J,SCORE_VALUE_MAX:()=>at,SET_SHIM:()=>z,SHARE_BYTES:()=>lt,SHARE_META_BYTES:()=>pt,SHIM:()=>V,SHIM_WAIT_MS:()=>At,audioVolumeOf:()=>oe,findShim:()=>Oe,hostTransport:()=>Te,init:()=>Pn,insetsFrom:()=>ge,isInputShim:()=>ye,isShim:()=>be,labelFor:()=>fe,preferredBinding:()=>de,servesScheme:()=>Re,shimTransport:()=>Se});var Mt=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;var Ue=9007199254740991;function ze(e){return typeof e=="string"&&e.length<=48&&Mt.test(e)}function ne(e){return typeof e=="number"&&Number.isFinite(e)&&Math.abs(e)<=9007199254740991}function Lt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.unlocked!="boolean")return null;let n={unlocked:t.unlocked};return typeof t.at=="number"&&Number.isFinite(t.at)&&(n.at=t.at),ne(t.progress)&&(n.progress=t.progress),n}function Ae(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.unlocked!="boolean"||typeof t.earned!="boolean")return null;let n={unlocked:t.unlocked,earned:t.earned};return ne(t.progress)&&(n.progress=t.progress),n}function Ge(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,o={ids:Array.isArray(t.ids)?t.ids.filter(ze):[]},r=Rt(t.state);return r!==void 0&&(o.state=r),o}function Be(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=Rt(e.state);return t===void 0?null:{state:t}}function Rt(e){if(e===null)return null;if(typeof e!="object"||e===void 0||Array.isArray(e))return;let t={};for(let[n,o]of Object.entries(e)){if(!ze(n))continue;let r=Lt(o);r!==null&&(t[n]=r)}return t}var Ct="hopple.",He=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function Ot(e){return typeof e=="string"&&!e.startsWith(Ct)&&He.test(e)}function We(e,t,n){let o=typeof e=="string"?e:"";if(!Ot(e))return{ok:!1,reason:"name",name:o};let r=n?.[e];if(n!==void 0&&r===void 0)return{ok:!1,reason:"undeclared",name:e};if(t!==void 0&&!_t(t))return{ok:!1,reason:"props",name:e};let s={},a=[];for(let[p,u]of Object.entries(_t(t)?t:{})){let d=r?.[p];if(r!==void 0&&d===void 0||!He.test(p)||!Bn(u,d)){a.push(p);continue}s[p]=u}return{ok:!0,name:e,props:s,dropped:a}}function _t(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function Bn(e,t){return typeof e=="string"?t===void 0||t==="string":typeof e=="number"?Number.isFinite(e)&&(t===void 0||t==="number"):typeof e=="boolean"?t===void 0||t==="boolean":!1}var re=1,q="master";var Tt=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function Nt(e){return typeof e=="string"&&e.length<=48&&Tt.test(e)}var K=Object.freeze({muted:!1,master:1,channels:Object.freeze({})});function Dt(e,t){return typeof e!="number"||!Number.isFinite(e)?t:Math.min(1,Math.max(0,e))}function we(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,n={};if(typeof t.channels=="object"&&t.channels!==null&&!Array.isArray(t.channels))for(let[o,r]of Object.entries(t.channels))!Nt(o)||o===q||typeof r!="number"||!Number.isFinite(r)||(n[o]=Math.min(1,Math.max(0,r)));return{muted:t.muted===!0,master:Dt(t.master,K.master),channels:n}}function Fe(e,t){if(e.muted!==t.muted||e.master!==t.master)return!1;let n=Object.keys(e.channels);return n.length!==Object.keys(t.channels).length?!1:n.every(o=>e.channels[o]===t.channels[o])}function oe(e,t){return e.muted?0:t===void 0||t===q?e.master:e.master*(e.channels[t]??1)}var Hn=Object.freeze({audio:!0,loop:!0,resources:!0});function Ve(e){let t=typeof e=="object"&&e!==null?e:{};return{audio:t.audio!==!1,loop:t.loop!==!1,resources:t.resources!==!1}}function se(e){if(typeof e!="object"||e===null)return!1;let t=e;if(t.p!==1)return!1;switch(t.kind){case"req":return typeof t.id=="string"&&typeof t.method=="string";case"res":return typeof t.id!="string"?!1:t.ok===!0?!0:t.ok===!1?Wn(t.error):!1;case"evt":return typeof t.topic=="string";default:return!1}}function Wn(e){if(typeof e!="object"||e===null)return!1;let t=e;return typeof t.code=="string"&&typeof t.message=="string"}var L=class extends Error{constructor(n){super(n.message);Pt(this,"code");this.name="BridgeError",this.code=n.code}};function Xe(e){return e instanceof L?{code:e.code,message:e.message}:{code:"internal",message:"The handler failed."}}function xe(e,t){let n,o;try{Fn(t)?(n=t.message===""?String(t.name):`${String(t.name)}: ${t.message}`,o=typeof t.stack=="string"?t.stack:void 0):typeof t=="string"?n=t:typeof t=="object"&&t!==null?n=JSON.stringify(t)??String(t):n=String(t)}catch{n="An error that could not be described."}let r={kind:e,message:jt(n,1e3)};return o!==void 0&&o!==""&&(r.stack=jt(o,8e3)),r}function Fn(e){if(e instanceof Error)return!0;let t=Object.prototype.toString.call(e);return(t==="[object Error]"||t==="[object DOMException]")&&typeof e.message=="string"}function jt(e,t){return e.length>t?`${e.slice(0,t-1)}\u2026`:e}var ke=["keyboard","mouse","touch","gamepad"];var Ut=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function Z(e){return typeof e=="string"&&e.length<=48&&Ut.test(e)}var zt=["row","column","diamond","cluster"],Gt=["left","right","full"];var Bt=["remap","scheme","host","setting"];function Ke(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(!Array.isArray(t.bind))return null;let o={bind:t.bind.filter(r=>typeof r=="string"&&r.length<=96)};return typeof t.title=="string"&&(o.title=t.title),typeof t.label=="string"&&(o.label=t.label),o}function Ze(e){let t=Wt(e);if(t===null)return null;let n=e,o={};for(let[s,a]of qe(n.actions)){if(!Z(s))continue;let p=Ke(a);p!==null&&(o[s]=p)}let r={};for(let[s,a]of qe(n.layouts)){if(!Z(s))continue;let p=Ht(a);p!==null&&(r[s]=p)}return{...t,actions:o,layouts:r}}function Ht(e){if(!Y(e))return null;let t={};for(let n of Gt){let o=e[n];if(!Y(o))continue;let r={};for(let[s,a]of Object.entries(o)){if(!Z(s))continue;let p=Vn(a);p!==null&&(r[s]=p)}t[n]=r}return t}function Vn(e){if(!Y(e))return null;let t=e.type;if(t!=="buttons"&&t!=="thumbstick")return null;let n={type:t},o=e.buttons;Array.isArray(o)&&(n.buttons=o.filter(Z));let r=e.shape;zt.includes(r)&&(n.shape=r);let s=e.as;if($t(s))n.as=s;else if(Y(s)){let p={};for(let[u,d]of Object.entries(s))Z(u)&&$t(d)&&(p[u]=d);n.as=p}let a=e.axis;return t==="thumbstick"&&(a==="x"||a==="y")&&(n.axis=a),n}function Ye(e){let t=Wt(e);if(t===null)return null;let n=e.cause;return{...t,cause:Bt.includes(n)?n:"host"}}function Wt(e){if(!Y(e))return null;let t=e,n={};for(let[o,r]of qe(t.mapping)){if(!Z(o))continue;let s=Ke(r);s!==null&&(n[o]=s)}return{mapping:n,scheme:ke.includes(t.scheme)?t.scheme:"keyboard",controls:t.controls===!0,sticks:t.sticks==="fixed"?"fixed":"floating"}}function Y(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function qe(e){return Y(e)?Object.entries(e):[]}function $t(e){return typeof e=="string"&&e.length<=96}var Je=["app","web","dev"],Qe=["low","mid","high"],B={app:{id:"",version:"",surface:"web"},game:{id:"",version:""},player:{id:null,returning:!1,locale:"en"},device:{tier:"mid"}};function R(e){let t=new Set;function n(o,r){try{return o.listener(...r)}catch(s){e(s);return}}return{on(o){let r={listener:o};return t.add(r),()=>{t.delete(r)}},emit(...o){for(let r of[...t]){if(!t.has(r))continue;let s=n(r,o);s instanceof Promise&&s.catch(e)}},async settle(...o){await Promise.all([...t].map(async r=>{if(t.has(r))try{await n(r,o)}catch(s){e(s)}}))},get size(){return t.size},clear(){t.clear()}}}var Ft=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;var ae=["about","scores","achievements","input"];function ie(e){return typeof e=="string"&&e.length<=32&&Ft.test(e)}function et(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,n={ids:Array.isArray(t.ids)?t.ids.filter(ie):[]};return(t.isOpen===!0||ie(t.page))&&(n.isOpen=!0),ie(t.page)&&(n.page=t.page),n}function tt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,n=ie(t.page)?t.page:null,o=t.isOpen===!0||n!==null;return{isOpen:o,page:o?n:null}}var nt=["running","paused","unpausable"],ce="unpausable";function rt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;return typeof t.paused=="boolean"?{paused:t.paused}:null}var J=65536;function st(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.state!="string"||typeof t.version!="number"||!Number.isFinite(t.version)||t.encoding!=="json"&&t.encoding!=="text")return null;let n={version:t.version,encoding:t.encoding,state:t.state};return typeof t.rev=="string"&&(n.rev=t.rev),n}function Ee(e){return new TextEncoder().encode(e).length}function Ie(e){if(typeof e!="object"||e===null)return null;let t=e;return!ot(t.at)||!ot(t.version)||!ot(t.bytes)?null:{at:t.at,version:t.version,bytes:t.bytes}}function ot(e){return typeof e=="number"&&Number.isFinite(e)}var at=9007199254740991,Vt=/^[a-z0-9]+(?:[._-][a-z0-9]+)*$/;function it(e){return typeof e=="string"&&e.length<=48&&Vt.test(e)}function ue(e){return typeof e=="number"&&Number.isFinite(e)&&Math.abs(e)<=9007199254740991}function Xt(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(!ue(t.best)||!Q(t.at)||!Q(t.runs))return null;let n={best:t.best,at:t.at,runs:t.runs};return Q(t.rank)&&(n.rank=t.rank),Q(t.of)&&(n.of=t.of),n}function Pe(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e,o={boards:Array.isArray(t.boards)?t.boards.filter(it):[]};if(t.standing===null)o.standing=null;else if(typeof t.standing=="object"&&t.standing!==void 0){let r={};for(let[s,a]of Object.entries(t.standing)){if(!it(s))continue;let p=Xt(a);p!==null&&(r[s]=p)}o.standing=r}return o}function ct(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(t.standing===null)return{standing:null};let n=Pe({boards:[],standing:t.standing});return n?.standing===void 0?null:{standing:n.standing}}function ut(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;let t=e;if(typeof t.improved!="boolean"||!ue(t.best))return null;let n={improved:t.improved,best:t.best};return Q(t.rank)&&(n.rank=t.rank),Q(t.of)&&(n.of=t.of),n}function Q(e){return typeof e=="number"&&Number.isFinite(e)}var lt=512,pt=1024;function dt(e){if(typeof e!="object"||e===null)return null;let t=e,n={};return typeof t.data=="string"&&t.data!==""&&(n.data=t.data),typeof t.offers=="boolean"&&(n.offers=t.offers),n}function ft(e){if(typeof e!="object"||e===null||Array.isArray(e))return null;try{let t=JSON.parse(JSON.stringify(e));return typeof t=="object"&&t!==null&&!Array.isArray(t)?t:null}catch{return null}}var Xn=1e4;function mt(e){let{transport:t}=e,n=e.timeoutMs??Xn,o=e.onError??(h=>console.error(h)),r=e.handlers,s=new Map,a=new Map,p=0,u=null,d=null,c=!1,i=t.receive(m);function m(h){c||(h.kind==="req"?f(h):h.kind==="res"?l(h):b(h))}async function f(h){if(w(h.id)){v({p:1,kind:"res",id:h.id,ok:!1,error:{code:"stale",message:"That conversation has ended; introduce yourself again."}});return}let x=r[h.method];if(!x){v({p:1,kind:"res",id:h.id,ok:!1,error:{code:"unknown_method",message:`No handler for "${h.method}".`}});return}try{let _=await x(h.params);if(c)return;v({p:1,kind:"res",id:h.id,ok:!0,value:_})}catch(_){if(c)return;v({p:1,kind:"res",id:h.id,ok:!1,error:Xe(_)})}}function l(h){let x=s.get(h.id);x&&(s.delete(h.id),clearTimeout(x.timer),h.ok?x.resolve(h.value):x.reject(new L(h.error)))}function b(h){a.get(h.topic)?.emit(h.data)}function v(h){try{t.send(h)}catch(x){o(x)}}function P(h,x,_){if(c)return Promise.reject(new L({code:"disposed",message:`"${h}" was not sent: peer disposed.`}));p+=1;let T=u===null?String(p):`${u}-${p}`,j=_?.timeoutMs??n;return new Promise((C,U)=>{let X=setTimeout(()=>{s.delete(T),U(new L({code:"timeout",message:`"${h}" got no reply within ${j}ms.`}))},j);s.set(T,{resolve:C,reject:U,timer:X}),v({p:1,kind:"req",id:T,method:h,params:x})})}function I(h,x){c||v({p:1,kind:"evt",topic:h,data:x})}function O(h,x){let _=a.get(h);_===void 0&&(_=R(o),a.set(h,_));let T=_.on(x),j=_;return()=>{T(),j.size===0&&a.get(h)===j&&a.delete(h)}}function A(h){for(let x of s.values())clearTimeout(x.timer),x.reject(h);s.clear()}function y(h){u=h}function k(h){c||(d=h,A(new L({code:"stale",message:"The other side started over; this call belonged to the conversation before it."})))}function w(h){if(d===null)return!1;let x=h.indexOf("-");return x===-1?!1:h.slice(0,x)!==d}function E(){c||(c=!0,i(),A(new L({code:"disposed",message:"The peer was disposed mid-call."})),a.clear(),t.close())}return{call:P,emit:I,on:O,useSession:y,expectSession:k,dispose:E}}var V="__HOPPLE_SHIM__",z="__HOPPLE_SET_SHIM__";var qt={version:"0.1.0-rc.2"};function Kt(e){return{...e,call:(n,o,r)=>e.call(n,o,r).catch(s=>{throw Kn(s)})}}function W(e,t,n){let o;try{o=e()}catch(r){o=Promise.reject(r)}o.then(n,r=>{r instanceof L&&(r.code==="unsupported"||r.code==="disposed")||t(r)})}function Kn(e){return!(e instanceof L)||e.code!=="unknown_method"?e:new L({code:"unsupported",message:e.message})}function Zt(e){let t=!1,n=[],o,r=yt(n,o),s=R(e.onError);function a(d){o=d;let c=r;r=yt(n,o),Zn(c,r)||s.emit(r,c)}function p(d,c){if(o===void 0)return;let i=o??{},m=i[d],f={unlocked:c.unlocked,...c.unlocked?{at:m?.at??Date.now()}:{},...c.progress===void 0?{}:{progress:c.progress}};a({...i,[d]:f})}return{achievements:{get available(){return t},get standing(){return r},on:s.on,unlock(d){t&&W(()=>e.peer().call("achievement.unlock",{id:d}),e.onError,c=>{p(d,Ae(c)??{unlocked:!0,earned:!0})})},progress(d,c){if(!ne(c)){e.onError(new Error(`achievements: progress on "${d}" must be a finite number an achievement can hold.`));return}t&&W(()=>e.peer().call("achievement.progress",{id:d,progress:c}),e.onError,i=>{p(d,Ae(i)??{unlocked:!1,earned:!1,progress:c})})}},adopt(d){let c=Ge(d);t=c!==null,n=c?.ids??[],o=c?.state,r=yt(n,o)},onChange(d){let c=Be(d);c!==null&&a(c.state)}}}function yt(e,t){if(t===void 0)return Object.freeze({known:!1,items:Object.freeze(e.map(r=>Object.freeze({id:r})))});let n=t??{},o=e.map(r=>Object.freeze({id:r,...n[r]??{unlocked:!1}}));return Object.freeze({known:!0,items:Object.freeze(o)})}function Zn(e,t){return e.known!==t.known||e.items.length!==t.items.length?!1:!e.known||!t.known?!0:e.items.every((n,o)=>{let r=t.items[o];return r!==void 0&&n.id===r.id&&n.unlocked===r.unlocked&&n.at===r.at&&n.progress===r.progress})}function Yt(e){return{refused(t){let n=t,o=typeof n=="object"&&n!==null?n:null;(e.onRefused??e.onError)(new Error(`analytics: the host refused "${String(o?.name)}" (${String(o?.reason)}).`))},analytics:{track(t,n){let o=We(t,n);if(!o.ok){e.onError(new Error(`analytics: "${o.name}" was not sent (${o.reason}).`));return}o.dropped.length>0&&e.onError(new Error(`analytics: "${o.name}" dropped ${o.dropped.join(", ")}.`));let r={name:o.name};Object.keys(o.props).length>0&&(r.props=o.props),e.peer().emit("analytics.event",r)}}}}var Jt=["pointerdown","pointerup","touchend","keydown"];function Qt(e){let t=ht(K),n=!0,o=!1,r=R(e.onError),s=new Map,a=new Map,p=!1;function u(l){return n?oe(t,l):0}function d(){for(let l of s.values())for(let[b,v]of l)v.gain.value=u(b);for(let[l,b]of a)l.volume=u(b.channel)}function c(){if(n){for(let l of s.keys())l.state==="suspended"&&l.resume().catch(()=>{});for(let[l,b]of a)b.stopped&&l.play().then(()=>{b.stopped=!1},()=>{})}}async function i(){for(let[l,b]of a)l.paused||(l.pause(),b.stopped=!0);await Promise.all([...s.keys()].map(l=>l.state==="running"?l.suspend().catch(()=>{}):void 0))}function m(){if(!(p||e.scope===null)){p=!0;for(let l of Jt)e.scope.addEventListener(l,c,{capture:!0})}}return{audio:{get allowed(){return n},get muted(){return t.muted},get master(){return t.master},get state(){return t},volume(l){return u(l)},channel(l){return l===q?t.master:t.channels[l]??1},on:r.on,output(l,b){let v=s.get(l);v===void 0&&(v=new Map,s.set(l,v),!n&&l.state==="running"&&l.suspend().catch(()=>{}));let P=v.get(b);return P===void 0&&(P=l.createGain(),P.gain.value=u(b),P.connect(l.destination),v.set(b,P)),m(),c(),P},media(l,b){if(a.set(l,{channel:b,stopped:!1}),l.volume=u(b),!n&&!l.paused){l.pause();let v=a.get(l);v!==void 0&&(v.stopped=!0)}return m(),()=>{a.delete(l)}}},async onAllow(l){return o=!0,l.allowed!==n&&(n=l.allowed,d(),r.emit(),n?c():await i()),{allowed:n}},dispose(){if(p&&e.scope!==null)for(let l of Jt)e.scope.removeEventListener(l,c,{capture:!0});p=!1,s.clear(),a.clear()},adopt(l,b){o||(n=b),t=ht(we(l)??K),d()},onSet(l){let b=ht(we(l)??t);return Fe(t,b)||(t=b,d(),r.emit()),t}}}function ht(e){return Object.freeze({muted:e.muted,master:e.master,channels:Object.freeze({...e.channels})})}var Yn=20,Jn=1e3,Qn=32;function en(e){let t=null,n=[],o=new Map;function r(c){if(t!==null){t.emit("error.report",c);return}n.length<Yn&&n.push(c)}function s(c,i){let m={error:i,again:0,timer:setTimeout(()=>{o.delete(c),m.again!==0&&(r({...i,count:m.again}),s(c,i))},Jn)};o.set(c,m)}function a(c){let i=`${c.kind}
|
|
3
3
|
${c.message}
|
|
4
|
-
${c.stack??""}`,m=o.get(i);if(m!==void 0){m.again+=1;return}r(c),o.size<Qn&&s(i,c)}function
|
|
4
|
+
${c.stack??""}`,m=o.get(i);if(m!==void 0){m.again+=1;return}r(c),o.size<Qn&&s(i,c)}function p(c,i){a(xe(c,i))}let u=c=>{let i=c;if(i.error!==void 0&&i.error!==null){p("uncaught",i.error);return}if(typeof i.message!="string"||i.message==="")return;let m=xe("uncaught",i.message);typeof i.filename=="string"&&i.filename!==""&&(m.stack=`at ${i.filename}:${i.lineno}:${i.colno}`),a(m)},d=c=>{p("rejection",c.reason)};return e?.addEventListener("error",u),e?.addEventListener("unhandledrejection",d),{report:p,connect(c){t=c;let i=n;n=[];for(let m of i)c.emit("error.report",m)},dispose(){e?.removeEventListener("error",u),e?.removeEventListener("unhandledrejection",d);for(let{error:c,again:i,timer:m}of o.values())clearTimeout(m),i>0&&r({...c,count:i});o.clear(),n=[]}}}function nn(e){let t=le(e),n=le(t.app),o=le(t.game),r=le(t.player),s=le(t.device);return Object.freeze({app:Object.freeze({id:pe(n.id,B.app.id),version:pe(n.version,B.app.version),surface:tn(n.surface,Je,B.app.surface)}),game:Object.freeze({id:pe(o.id,B.game.id),version:pe(o.version,B.game.version)}),player:Object.freeze({id:typeof r.id=="string"&&r.id!==""?r.id:null,returning:r.returning===!0,locale:pe(r.locale,B.player.locale)}),device:Object.freeze({tier:tn(s.tier,Qe,B.device.tier)})})}function le(e){return typeof e=="object"&&e!==null?e:{}}function pe(e,t){return typeof e=="string"?e:t}function tn(e,t,n){return t.includes(e)?e:n}var ee=["pad.right.south","pad.right.east","pad.right.west","pad.right.north","pad.left.bumper","pad.right.bumper","pad.left.trigger","pad.right.trigger","pad.select","pad.start",null,null,"pad.left.north","pad.left.south","pad.left.west","pad.left.east"],gt=["pad.left.stick","pad.right.stick"],er=["pad.left.stick","pad.right.stick"];function Me(e,t){if(e===t)return!0;if(e.startsWith("pad.")&&t.startsWith("pad.")){let n=e.slice(4),o=t.slice(4).split(".");return o.length===2&&o[1]===n}return e==="pointer.primary"?t==="mouse.left"||t==="touch.tap":e==="pointer.secondary"?t==="mouse.right":!1}function Le(e,t){return e===t?!0:e==="pad.stick"&&er.includes(t)}function de(e,t){return e.find(n=>Re(n,t))??e[0]}function Re(e,t){if(e.startsWith("pointer."))return t==="mouse"||t==="touch";if(e.startsWith("controls."))return t==="touch";switch(t){case"keyboard":return e.startsWith("key.");case"gamepad":return e.startsWith("pad.");case"mouse":return e.startsWith("mouse.");case"touch":return e.startsWith("touch.");default:return!1}}function fe(e,t){if(e.startsWith("key."))return nr(e.slice(4));if(e==="mouse.left")return"Click";if(e==="mouse.right")return"Right click";if(e==="mouse.middle")return"Middle click";if(e==="touch.tap")return"Tap";if(e==="pointer.primary")return t==="touch"?"Tap":"Click";if(e==="pointer.secondary")return"Right click";if(!e.startsWith("pad."))return"";let n=e.slice(4).split("."),[o,r]=n;if(n.length===1)return o==="start"?"Start":o==="select"?"Select":rn[o]??"";if(o==="left"&&r!==void 0){let s=tr[r];return s!==void 0?s:r==="bumper"?"LB":r==="trigger"?"LT":""}if(o==="right"&&r!==void 0){let s=rn[r];return s!==void 0?s:r==="bumper"?"RB":r==="trigger"?"RT":""}return""}var rn={south:"A",east:"B",west:"X",north:"Y"},tr={north:"\u2191",south:"\u2193",west:"\u2190",east:"\u2192"};function nr(e){return e.startsWith("Key")&&e.length===4?e.slice(3):e.startsWith("Digit")&&e.length===6?e.slice(5):e.startsWith("Numpad")?`Num ${e.slice(6)}`:{ArrowUp:"\u2191",ArrowDown:"\u2193",ArrowLeft:"\u2190",ArrowRight:"\u2192",Space:"Space",Enter:"Enter",Tab:"Tab",Backspace:"Backspace",ShiftLeft:"Shift",ShiftRight:"Shift",ControlLeft:"Ctrl",ControlRight:"Ctrl",AltLeft:"Alt",AltRight:"Alt"}[e]??e}var me="__HOPPLE_INPUT__";function ye(e){return typeof e!="object"||e===null?!1:e.v===1}var he=4,rr=["top","right","bottom","left"],or=["row","column","diamond","cluster"],sr=["left","right","full"];function on(e,t,n,o,r,s){let a=[];for(let p of sr){let u=t[p];if(u===void 0)continue;let d=[];for(let[c,i]of Object.entries(u))d.push(ir(c,i,n));d.length>0&&a.push({zone:p,controls:d})}return{layout:e,zones:a,frame:o,insets:r,sticks:s}}function ir(e,t,n){let o=or.includes(t.shape)?t.shape:"column";if(t.type==="thumbstick"){let a=typeof t.as=="string"?t.as:`controls.${e}`,p={id:e,type:"thumbstick",shape:o,buttons:[],stick:a};return t.axis!==void 0&&(p.axis=t.axis),p}let r=typeof t.as=="object"&&t.as!==null?t.as:{},s=(t.buttons??[]).map(a=>{let p={action:a,label:ar(n[a])??a},u=r[a];return typeof u=="string"&&(p.as=u),p});return{id:e,type:"buttons",shape:o,buttons:s,stick:""}}function ar(e){let t=e?.label;return typeof t=="string"&&t!==""?t:void 0}function sn(e){let{scope:t}=e,n=t.document,o=null,r=null,s=null,a=!1;function p(){return r!==null||(o=n.createElement("div"),o.setAttribute("data-hopple-controls",""),o.style.cssText="position:fixed;inset:0;pointer-events:none;z-index:2147483000;touch-action:none;",r=o.attachShadow({mode:"open"}),(n.body??n.documentElement).append(o)),r}return{render(u){this.clear();let d=p();for(let i of rr)o?.style.setProperty(`--hopple-pad-${i}`,`${u.insets[i]}px`);let c=e.shim();if(c!==null&&!a&&typeof c.style=="function")try{c.style(d,{layout:u.layout,frame:u.frame}),a=!0}catch(i){e.onError(i)}if(c!==null&&typeof c.render=="function")try{let i=c.render(d,u,e.report);s=typeof i=="function"?i:null;return}catch(i){e.onError(i),d.replaceChildren(),s=null}s=cr(d,u,e.report,n,e.onError)},clear(){if(s!==null){try{s()}catch(u){e.onError(u)}s=null}r?.replaceChildren()},rects(){let u=[];if(r===null)return u;for(let d of r.querySelectorAll("[data-control]")){let c=d.getBoundingClientRect();c.width<=0||c.height<=0||u.push({x:c.x,y:c.y,width:c.width,height:c.height})}return u},fits(u,d){if(u.width<=0||u.height<=0)return!0;let c=u.width-d.right,i=u.height-d.bottom;for(let m of this.rects())if(m.x<d.left||m.y<d.top||m.x+m.width>c||m.y+m.height>i)return!1;return!0},owns(u){return o===null?!1:(typeof u.composedPath=="function"?u.composedPath():[]).includes(o)?!0:u.target===o},dispose(){this.clear(),o?.remove(),o=null,r=null}}}function cr(e,t,n,o,r){let s=o.createElement("style");s.textContent=pr,e.append(s);let a=[],p=t.sticks==="floating",u=()=>e.host.setAttribute("data-used","");for(let{zone:d,controls:c}of t.zones){let i=o.createElement("div");i.className=`zone zone-${d}`;for(let m of c){if(m.type==="buttons"){i.append(ur(m,n,o,a,r));continue}let f=lr(m,p?d:null,n,u,o,a,r);i.append(f.well),f.reach!==null&&s.after(f.reach)}e.append(i)}return()=>{for(let d of a)d()}}function ur(e,t,n,o,r){let s=n.createElement("div");s.className=`control buttons shape-${e.shape}`,s.setAttribute("data-control",e.id);for(let[a,p]of e.buttons.entries()){let u=n.createElement("button");u.className="btn",u.type="button",u.textContent=p.label,u.setAttribute("aria-label",p.action),u.setAttribute("data-action",p.action);let d=m=>f=>{f.preventDefault(),f.stopPropagation(),u.classList.toggle("down",m);try{t.press(e.id,a,m)}catch(l){r(l)}},c=d(!0),i=d(!1);u.addEventListener("pointerdown",c),u.addEventListener("pointerup",i),u.addEventListener("pointercancel",i),u.addEventListener("pointerleave",i),o.push(()=>{u.removeEventListener("pointerdown",c),u.removeEventListener("pointerup",i),u.removeEventListener("pointercancel",i),u.removeEventListener("pointerleave",i)}),s.append(u)}return s}function lr(e,t,n,o,r,s,a){let{axis:p}=e,u=r.createElement("div");if(u.className=p===void 0?"control stick":`control stick track track-${p}`,t!==null&&u.classList.add("floating"),u.setAttribute("data-control",e.id),u.setAttribute("aria-label",e.stick),p!==void 0)for(let A of["start","end"]){let y=r.createElement("span");y.className=`chevron chevron-${A}`,u.append(y)}let d=r.createElement("div");d.className="knob",u.append(d);let c=null;t!==null&&(c=r.createElement("div"),c.className=`reach reach-${t}`,c.setAttribute("data-reach",e.id));let i=null,m=null,f=null;function l(A,y){d.style.setProperty("--x",String(A)),d.style.setProperty("--y",String(y));try{n.move(e.id,A,y)}catch(k){a(k)}}function b(A){f=A;let y=m??A;u.style.setProperty("--dx",`${A.x-y.x}px`),u.style.setProperty("--dy",`${A.y-y.y}px`)}let v=A=>{let y=A;if(A.preventDefault(),A.stopPropagation(),t!==null&&m===null){let k=u.getBoundingClientRect();m={x:k.x+k.width/2,y:k.y+k.height/2}}i=y.pointerId;try{A.currentTarget?.setPointerCapture?.(y.pointerId)}catch{}u.classList.add("held"),t!==null&&(b({x:y.clientX,y:y.clientY}),o()),P(y)},P=A=>{let y=u.getBoundingClientRect(),k=f??{x:y.x+y.width/2,y:y.y+y.height/2};if(p!==void 0){let _=p==="x"?y.width:y.height,T=p==="x"?y.height:y.width,j=_>T?(_-T)/2:_/2;if(j<=0)return;let C=p==="x"?A.clientX:A.clientY,U=C-(p==="x"?k.x:k.y),X=Math.max(-1,Math.min(1,U/j));if(f!==null&&Math.abs(U)>j){let F=C-X*j;b(p==="x"?{x:F,y:f.y}:{x:f.x,y:F})}l(p==="x"?X:0,p==="y"?X:0);return}let w=Math.min(y.width,y.height)/2;if(w<=0)return;let E=(A.clientX-k.x)/w,h=(A.clientY-k.y)/w,x=Math.hypot(E,h);x>1&&(E/=x,h/=x,f!==null&&b({x:A.clientX-E*w,y:A.clientY-h*w})),l(E,h)},I=A=>{let y=A;i===y.pointerId&&(A.preventDefault(),A.stopPropagation(),P(y))},O=A=>{i===A.pointerId&&(A.stopPropagation(),i=null,u.classList.remove("held"),t!==null&&(f=null,u.classList.add("let-go")),l(0,0))};for(let A of c===null?[u]:[u,c])A.addEventListener("pointerdown",v),A.addEventListener("pointermove",I),A.addEventListener("pointerup",O),A.addEventListener("pointercancel",O),s.push(()=>{A.removeEventListener("pointerdown",v),A.removeEventListener("pointermove",I),A.removeEventListener("pointerup",O),A.removeEventListener("pointercancel",O)});return{well:u,reach:c}}var pr=`
|
|
5
5
|
:host, * { box-sizing: border-box; }
|
|
6
6
|
:host {
|
|
7
7
|
--hopple-pad-size: 56px;
|
|
@@ -26,9 +26,10 @@ ${c.stack??""}`,m=o.get(i);if(m!==void 0){m.again+=1;return}r(c),o.size<Qn&&s(i,
|
|
|
26
26
|
--hopple-pad-radius: 999px;
|
|
27
27
|
/* How high a floating stick's side of the screen reaches, from the bottom. */
|
|
28
28
|
--hopple-pad-reach: 60%;
|
|
29
|
-
/* A floating stick at rest is a
|
|
30
|
-
the stick under a thumb. */
|
|
31
|
-
--hopple-pad-idle: 0.
|
|
29
|
+
/* A floating stick at rest is a mark that there is one, drawn quieter than
|
|
30
|
+
the stick under a thumb, and quieter again once the player has used it. */
|
|
31
|
+
--hopple-pad-idle: 0.55;
|
|
32
|
+
--hopple-pad-used: 0.3;
|
|
32
33
|
}
|
|
33
34
|
.zone {
|
|
34
35
|
position: fixed;
|
|
@@ -81,7 +82,11 @@ ${c.stack??""}`,m=o.get(i);if(m!==void 0){m.again+=1;return}r(c),o.size<Qn&&s(i,
|
|
|
81
82
|
.knob {
|
|
82
83
|
width: 44%; height: 44%; border-radius: 50%;
|
|
83
84
|
background: var(--hopple-pad-edge);
|
|
84
|
-
|
|
85
|
+
/* As far as the thumb has gone: all the way over is the knob on the rim. */
|
|
86
|
+
transform: translate(
|
|
87
|
+
calc(var(--x, 0) * var(--hopple-pad-stick) / 2),
|
|
88
|
+
calc(var(--y, 0) * var(--hopple-pad-stick) / 2)
|
|
89
|
+
);
|
|
85
90
|
will-change: transform;
|
|
86
91
|
}
|
|
87
92
|
.track {
|
|
@@ -127,13 +132,24 @@ ${c.stack??""}`,m=o.get(i);if(m!==void 0){m.again+=1;return}r(c),o.size<Qn&&s(i,
|
|
|
127
132
|
.reach-left { left: var(--hopple-pad-left); right: 50%; }
|
|
128
133
|
.reach-right { left: 50%; right: var(--hopple-pad-right); }
|
|
129
134
|
.reach-full { left: var(--hopple-pad-left); right: var(--hopple-pad-right); }
|
|
130
|
-
.floating { translate: var(--dx, 0px) var(--dy, 0px); }
|
|
131
|
-
|
|
135
|
+
.floating.held { translate: var(--dx, 0px) var(--dy, 0px); }
|
|
136
|
+
/* At rest a mark and not a stick: an outline and a dot, with nothing on it that
|
|
137
|
+
looks like it has to be pressed there. */
|
|
138
|
+
.floating:not(.held) { opacity: var(--hopple-pad-idle); background: transparent; }
|
|
139
|
+
:host([data-used]) .floating:not(.held) { opacity: var(--hopple-pad-used); }
|
|
140
|
+
.floating:not(.held) .knob { scale: 0.3; }
|
|
141
|
+
.floating.track:not(.held) .knob { scale: 0; }
|
|
132
142
|
@media (prefers-reduced-motion: no-preference) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
143
|
+
/* Back to the middle gently; under a thumb at once, since it is already there. */
|
|
144
|
+
.stick:not(.held) .knob { transition: transform 60ms linear, scale 60ms linear; }
|
|
145
|
+
/* Let go, it goes out where the thumb left it and the mark comes back where
|
|
146
|
+
it rests, rather than travelling there as if that were where it lives. */
|
|
147
|
+
.floating.let-go:not(.held) { animation: let-go 280ms ease-out; }
|
|
148
|
+
}
|
|
149
|
+
@keyframes let-go {
|
|
150
|
+
0% { translate: var(--dx, 0px) var(--dy, 0px); opacity: 1; }
|
|
151
|
+
40% { translate: var(--dx, 0px) var(--dy, 0px); opacity: 0; }
|
|
152
|
+
41% { translate: 0px 0px; opacity: 0; }
|
|
137
153
|
}
|
|
138
|
-
`;function dr(e){let t=e.document.activeElement;if(t===null)return!1;if(t.isContentEditable)return!0;let n=t.tagName;return n==="INPUT"||n==="TEXTAREA"||n==="SELECT"}function an(e){let{scope:t,store:n}=e,o=t.document,r=[],s=[],a=!1,u=null,p=new Map,d=new Set,c=!0;function i(y,E,k,P,g=!1){let w=O=>{try{k(O)}catch(T){e.onError(T)}};y.addEventListener(E,w,g),P.push(()=>y.removeEventListener(E,w,g))}function m(y,E){let k=y;if(E&&k.repeat||e.suspended()||dr(t))return;let P=`key.${k.code}`;e.isBound(P)&&(k.preventDefault(),e.onScheme("keyboard"),n.press(P,E))}function f(y){return y.pointerType!=="mouse"?"touch.tap":y.button===0?"mouse.left":y.button===1?"mouse.middle":y.button===2?"mouse.right":null}function l(y,E){let k=y;if(e.fromControls(y))return;if(e.onScheme(k.pointerType==="mouse"?"mouse":"touch"),E==="move"){n.movePointer({x:k.clientX,y:k.clientY,down:n.pointer?.down??!1});return}let P=E==="down";if(n.movePointer({x:k.clientX,y:k.clientY,down:P}),e.suspended())return;let g=f(k);g!==null&&n.press(g,P),(g==="touch.tap"||g==="mouse.left")&&n.press("pointer.primary",P)}function b(y,E){let k=e.deadzone(),P=Math.hypot(y,E);if(P<=k)return{x:0,y:0};if(k<=0||P===0)return{x:y,y:E};let g=(P-k)/(1-k)/P;return{x:y*g,y:E*g}}function v(){for(let y of t.navigator.getGamepads?.()??[])if(y!==null){if(y.mapping==="standard"){let E=p.get(y.index)??[],k=[];for(let[P,g]of y.buttons.entries()){let w=g.pressed;if(k[P]=w,w===(E[P]??!1)||!c)continue;let O=J[P];O!=null&&(e.onScheme("gamepad"),e.suspended()||n.press(O,w))}p.set(y.index,k)}for(let[E,k]of ht.entries()){let P=c?b(y.axes[E*2]??0,y.axes[E*2+1]??0):{x:0,y:0};(P.x!==0||P.y!==0)&&e.onScheme("gamepad"),n.moveStick(k,P.x,P.y)}}}function M(){let y=()=>{u=null;try{v()}catch(E){e.onError(E)}d.size>0&&u===null&&M()};u=typeof t.requestAnimationFrame=="function"?t.requestAnimationFrame(y):setTimeout(y,16)}function I(){u!==null&&(typeof t.cancelAnimationFrame=="function"?t.cancelAnimationFrame(u):clearTimeout(u),u=null)}function A(y){d.add(y),u===null&&M()}function x(y){d.delete(y);let E=p.get(y)??[];p.delete(y);for(let[k,P]of E.entries()){let g=J[k];P&&g!==null&&g!==void 0&&n.press(g,!1)}for(let k of ht)n.moveStick(k,0,0);d.size===0&&I()}return{attach(){i(t,"keydown",y=>m(y,!0),r),i(t,"keyup",y=>m(y,!1),r),i(t,"pointerdown",y=>l(y,"down"),r),i(t,"pointerup",y=>l(y,"up"),r),i(t,"pointercancel",y=>l(y,"up"),r),i(t,"blur",()=>{c=!1,n.releaseAll()},r),i(t,"focus",()=>{c=!0},r),i(o,"visibilitychange",()=>{o.visibilityState==="hidden"&&n.releaseAll()},r),i(t,"contextmenu",y=>{e.isBound("mouse.right")&&y.preventDefault()},r),i(t,"gamepadconnected",y=>{A(y.gamepad.index)},r),i(t,"gamepaddisconnected",y=>{x(y.gamepad.index)},r);for(let y of t.navigator.getGamepads?.()??[])y!==null&&A(y.index)},watchPointer(y){if(y!==a){if(a=y,y){i(t,"pointermove",E=>l(E,"move"),s),i(o,"pointerleave",()=>n.movePointer(null),s);return}for(let E of s.splice(0))E()}},detach(){this.watchPointer(!1);for(let y of r.splice(0))y();I(),d.clear(),p.clear()}}}var cn={x:0,y:0};function un(e){let t={},n=new Map,o=new Set,r=new Set,s=new Map,a=null,u=new Map,p=new Map,d=_(e.onError);function c(f,l){let b=f.get(l);return b===void 0&&(b=_(e.onError),f.set(l,b)),b}function i(f,l){(l?o:r).add(f),u.get(f)?.emit(l,f)}function m(f,l,b){let v=n.get(f);v===void 0&&(v=new Set,n.set(f,v));let M=v.size>0;b?v.add(l):v.delete(l);let I=v.size>0;M!==I&&i(f,I)}return{setMapping(f,l){t=f,l?.release!==!1&&this.releaseAll()},get mapping(){return t},press(f,l,b){for(let[v,M]of Object.entries(t))M.bind.some(I=>Pe(I,f))&&m(v,b??f,l)},pressAction(f,l,b){m(f,l,b)},moveStick(f,l,b){let v=s.get(f);if(v!==void 0&&v.x===l&&v.y===b)return;let M={x:l,y:b};s.set(f,M);for(let[I,A]of p)Me(I,f)&&A.emit(M,f)},movePointer(f){f===null&&a===null||f!==null&&a!==null&&f.x===a.x&&f.y===a.y&&f.down===a.down||(a=f,d.emit(f))},releaseAll(){for(let[f,l]of n)l.size!==0&&(l.clear(),i(f,!1));for(let f of[...s.keys()])this.moveStick(f,0,0);this.movePointer(null)},releaseFrom(f){for(let[l,b]of n)if(b.size!==0){for(let v of[...b])f(v)&&b.delete(v);b.size===0&&i(l,!1)}},pressed(f){return(n.get(f)?.size??0)>0},stick(f){let l=s.get(f);if(l!==void 0)return l;for(let[b,v]of s)if(Me(f,b)&&(v.x!==0||v.y!==0))return v;return cn},get pointer(){return a},read(){let f=o,l=r;o=new Set,r=new Set;let b=new Set;for(let[I,A]of n)A.size>0&&b.add(I);let v=new Map(s),M=a===null?null:{...a};return Object.freeze({pressed:I=>b.has(I),started:I=>f.has(I),ended:I=>l.has(I),stick(I){let A=v.get(I);if(A!==void 0)return A;for(let[x,y]of v)if(Me(I,x)&&(y.x!==0||y.y!==0))return y;return cn},pointer:M})},onAction(f,l){return c(u,f).on(l)},onStick(f,l){return c(p,f).on(l)},onPointer(f){return d.on(f)},get watchingPointer(){return d.size>0},dispose(){u.clear(),p.clear(),d.clear(),n.clear(),s.clear(),a=null}}}var Re=.15;function pn(e){let t=e.scope,n={},o={},r=un({onError:e.onError}),s=Object.freeze({}),a={},u="keyboard",p="keyboard",d=!1,c="floating",i=!1,m=!1,f={scheme:p},l=null,b=!1,v=null,M=_(e.onError),I=!1,A=new Set;function x(S,h){let L=S==="action"?h in n||h in a:h in o;!I||L||A.has(`${S}:${h}`)||(A.add(`${S}:${h}`),e.onError(new Error(`input: the manifest declares no ${S} "${h}".`)))}function y(){if(t===null)return null;let S=t[fe];return me(S)?S:null}function E(S,h){let L=y();if(L===null)return h;try{let N=S(L);return N===void 0?h:N}catch(N){return e.onError(N),h}}function k(){let S=E(h=>h.deadzone,Re);return typeof S=="number"&&S>=0&&S<1?S:Re}function P(){let S={},h=!1;for(let[D,z]of Object.entries(n)){let De=a[D],je=De?.bind??z.bind,$e={bind:je},Et=De?.title??z.title;typeof Et=="string"&&($e.title=Et);let It=g(D,je,De);It!==""&&($e.label=It),S[D]=$e,dn(s[D]?.bind,je)||(h=!0)}for(let[D,z]of Object.entries(a))S[D]===void 0&&(S[D]=z,h=!0);for(let D of Object.keys(s))S[D]===void 0&&(h=!0);let L=s;s=Object.freeze(S),r.setMapping(S,{release:h});let N=f,j=N.scheme!==p||!fr(L,s);f={scheme:p},j&&M.emit(Object.freeze({mapping:s,scheme:p}),Object.freeze({mapping:L,scheme:N.scheme}))}function g(S,h,L){if(L?.label!==void 0&&u===p)return L.label;let N=pe(h,p),j=N===void 0?"":de(N,p);if(j!=="")return j;let D=n[S]?.label;return typeof D=="string"?D:""}function w(S){S!==p&&(p=S,P())}let T=t===null?null:sn({scope:t,report:{press(S,h,L){let j=v?.zones.flatMap(z=>z.controls).find(z=>z.id===S)?.buttons[h];if(j===void 0||(w("touch"),i))return;let D=`${ln}${S}.${h}`;r.pressAction(j.action,D,L),j.as!==void 0&&r.press(j.as,L,`${D}.as`),E(z=>z.haptic?.(L?"press":"release"),void 0)},move(S,h,L){let N=v?.zones.flatMap(j=>j.controls).find(j=>j.id===S);N!==void 0&&(w("touch"),!i&&(N.axis==="x"?r.moveStick(N.stick,h,0):N.axis==="y"?r.moveStick(N.stick,0,L):r.moveStick(N.stick,h,L)))}},shim:y,onError:e.onError}),$=t===null?null:an({scope:t,store:r,onScheme:w,isBound:C,suspended:()=>i,deadzone:k,fromControls:S=>T?.owns(S)??!1,onError:e.onError});function C(S){for(let h of Object.values(s))if(h.bind.some(L=>Pe(L,S)))return!0;return!1}function G(){$?.watchPointer(m||r.watchingPointer)}function Te(){let S=Object.keys(o);return S.length===1?S[0]:null}function ee(){let S=e.frame(),h=e.insets(),L=l,N=L===null?void 0:o[L];if(!(T!==null&&L!==null&&N!==void 0&&d&&E(D=>D.decide?.({layout:L,frame:S,insets:h,contract:ye}),!0)!==!1)||T===null||L===null||N===void 0){kt();return}if(v=on(L,N,n,S,h,c),T.render(v),!T.fits(S,h)){kt();return}b=!0,e.setDrawn(T.rects())}function kt(){!b&&v===null||(T?.clear(),v=null,b=!1,e.setDrawn([]))}function Ne(){r.releaseFrom(S=>S.startsWith(ln));for(let S of v?.zones??[])for(let h of S.controls)h.type==="thumbstick"&&r.moveStick(h.stick,0,0)}let On={show(S){let h=S??Te();h!==null&&(x("layout",h),h!==l&&Ne(),l=h,ee())},hide(){l!==null&&(Ne(),l=null,ee())},get showing(){return b},get asked(){return l},get available(){return d}},Tn={onAction(S,h){return x("action",S),r.onAction(S,h)},onStick:(S,h)=>r.onStick(S,h),onPointer(S){let h=r.onPointer(S);return G(),()=>{h(),G()}},get mapping(){return s},on:M.on,get scheme(){return p},controls:On,get poll(){return m},set poll(S){m=S,G()},read(){let S=r.read();return Object.freeze({pressed:h=>(x("action",h),S.pressed(h)),started:h=>(x("action",h),S.started(h)),ended:h=>(x("action",h),S.ended(h)),stick:h=>S.stick(h),pointer:S.pointer})},suspend(){i||(i=!0,r.releaseAll())},resume(){i=!1},get suspended(){return i}};return P(),$?.attach(),{input:Tn,adopt(S){let h=Ze(S);h!==null&&(I=!0,n=h.actions,o=h.layouts,a=h.mapping,u=h.scheme,p=h.scheme,d=h.controls,c=h.sticks,P(),ee())},onChange(S){let h=Ye(S);if(h===null)return;a=h.mapping,u=h.scheme,p=h.scheme;let L={available:d,sticks:c};d=h.controls,c=h.sticks,P(),L.sticks!==c&&Ne(),(L.available!==d||L.sticks!==c)&&ee()},relayout(){ee()},release:()=>r.releaseAll(),dispose(){$?.detach(),T?.dispose(),r.dispose(),M.clear()}}}var ln="controls.";function dn(e,t){return e===void 0?!1:e.length===t.length&&e.every((n,o)=>n===t[o])}function fr(e,t){let n=Object.keys(e);return n.length!==Object.keys(t).length?!1:n.every(o=>{let r=e[o],s=t[o];return r===void 0||s===void 0?!1:r.title===s.title&&r.label===s.label&&dn(r.bind,s.bind)})}var mr={top:0,right:0,bottom:0,left:0},yr=["top","right","bottom","left"],bt={width:0,height:0};function fn(e){let t=bt,n=[],o=[],r=[],s=null,a=_(e.onError);function u(){return s??(s=ge(r,t)),s}function p(){return{frame:t,obstructions:r,insets:u()}}function d(){if(e.root===null)return;let m=u();for(let f of yr)e.root.style.setProperty(`--hopple-inset-${f}`,`${m[f]}px`)}function c(m){let f=p(),l=Sr(m);t=l.frame,n=l.obstructions,i(f)}function i(m){if(r=o.length===0?n:[...n,...o],s=null,d(),m.frame.width===t.width&&m.frame.height===t.height&&br(m.obstructions,r))return;let f=p();a.emit(f,m)}return{adopt:c,onChange:c,setDrawn(m){let f=p();o=m.map(l=>({rect:l,kind:"controls",reserved:!0})),i(f)},dispose(){a.clear()},layout:{get frame(){return t},get insets(){return u()},get obstructions(){return r},on:a.on}}}function ge(e,t,n={}){let o={...mr};if(t.width<=0||t.height<=0)return o;let r=n.only==="strips";for(let s of e){if(!s.reserved)continue;let a=hr(s.rect,t);if(a===null||r&&!gr(a,t))continue;let u=[["top",a.y+a.height,t.width],["bottom",t.height-a.y,t.width],["left",a.x+a.width,t.height],["right",t.width-a.x,t.height]],p=u[0];for(let d of u)d[1]*d[2]<p[1]*p[2]&&(p=d);o[p[0]]=Math.max(o[p[0]],p[1])}return o}var _e=1;function gr(e,t){return e.x<=_e&&e.x+e.width>=t.width-_e||e.y<=_e&&e.y+e.height>=t.height-_e}function hr(e,t){let n=Math.max(0,e.x),o=Math.max(0,e.y),r=Math.min(t.width,e.x+e.width),s=Math.min(t.height,e.y+e.height);return r<=n||s<=o?null:{x:n,y:o,width:r-n,height:s-o}}function br(e,t){return e===t?!0:e.length!==t.length?!1:e.every((n,o)=>{let r=t[o];return r!==void 0&&n.kind===r.kind&&n.reserved===r.reserved&&n.rect.x===r.rect.x&&n.rect.y===r.rect.y&&n.rect.width===r.rect.width&&n.rect.height===r.rect.height})}function Sr(e){let t=e;if(typeof t!="object"||t===null)return{frame:bt,obstructions:[]};let n=Ar(t.frame)?t.frame:bt;if(!Array.isArray(t.obstructions))return{frame:n,obstructions:[]};let o=[];for(let r of t.obstructions){if(typeof r!="object"||r===null)continue;let{rect:s,kind:a,reserved:u}=r;!vr(s)||typeof u!="boolean"||o.push({rect:s,reserved:u,kind:a??"chrome"})}return{frame:n,obstructions:o}}function vr(e){if(typeof e!="object"||e===null)return!1;let t=e;return Q(t.x)&&Q(t.y)&&Q(t.width)&&Q(t.height)}function Ar(e){if(typeof e!="object"||e===null)return!1;let t=e;return Q(t.width)&&Q(t.height)}function Q(e){return typeof e=="number"&&Number.isFinite(e)}function mn(e){let t="loading",n=!1,o=_(e.onError);async function r(s){let a=t;t=s,await o.settle(s,a)}return{async onDispose(){return t!=="disposed"&&await r("disposed"),{}},lifecycle:{get phase(){return t},ready(){t!=="loading"||n||(n=!0,W(()=>e.peer().call("lifecycle.ready",{}),s=>{n=!1,e.onError(s)},()=>{t==="loading"&&r("ready")}))},on:o.on}}}var St=.1;function yn(e){let t=!0,n=!1,o=!1,r=_(e.onError),s=new Set;function a(){return t&&!o}function u(i){i.stopped||i.handle!==0||!a()||(i.last=void 0,i.handle=e.frames.request(m=>d(i,m)))}function p(i){i.handle!==0&&(e.frames.cancel(i.handle),i.handle=0)}function d(i,m){i.handle=0;let f=i.last===void 0?0:Math.min(Math.max(m-i.last,0)/1e3,St);i.last=m;try{i.frame(f,m)}catch(l){e.onError(l)}i.handle===0&&!i.stopped&&a()&&(i.handle=e.frames.request(l=>d(i,l)))}function c(){for(let i of s)a()?u(i):p(i)}return{adopt(i){n||(t=i),c()},async onAllow(i){return n=!0,i.allowed!==t&&(t=i.allowed,c(),await r.settle(t)),{allowed:t}},dispose(){o=!0;for(let i of s)i.stopped=!0,p(i);s.clear()},loop:{get allowed(){return t},on:r.on,tick(i){let m={frame:i,handle:0,last:void 0,stopped:!1};return s.add(m),u(m),()=>{m.stopped=!0,p(m),s.delete(m)}}}}}var wr=Object.freeze({isOpen:!1,page:null});function hn(e){let t=[],n=wr,o=_(e.onError),r={get available(){return t.length>0},get pages(){return t},get isOpen(){return n.isOpen},get page(){return n.page},on:o.on,open(a){W(()=>e.peer().call("panel.open",{id:a}),e.onError)}};function s(a){if(a.isOpen===n.isOpen&&a.page===n.page)return;let u=n;n=Object.freeze({isOpen:a.isOpen,page:a.page}),o.emit(n,u)}return{panel:r,adopt(a){let u=et(a);t=(u?.ids??[]).filter(bn),n=Object.freeze({isOpen:u?.isOpen===!0,page:gn(u?.page)})},onChange(a){let u=tt(a);u!==null&&s({isOpen:u.isOpen,page:gn(u.page)})},dispose(){o.clear()}}}function bn(e){return ie.includes(e)}function gn(e){return typeof e=="string"&&bn(e)?e:null}function Sn(e){let t=ae,n=_(e.onError);async function o(s){await n.settle(s)}return{pause:{get state(){return t},set(s){s!==t&&(t=s,e.peer().emit("pause.change",{state:t}))},on:n.on},async onSet(s){let a=rt(s);return a!==null&&await o(a.paused),{state:t}}}}function vn(e){let t=!0,n=!1,o=_(e.onError);return{adopt(r){n||(t=r)},async onAllow(r){return n=!0,r.allowed!==t&&(t=r.allowed,await o.settle(t)),{allowed:t}},resources:{get allowed(){return t},on:o.on}}}var xr=1,kr=[{what:"a Map",is:e=>e instanceof Map,becomes:"{}"},{what:"a Set",is:e=>e instanceof Set,becomes:"{}"},{what:"a Date",is:e=>e instanceof Date,becomes:"a string"},{what:"a typed array",is:e=>ArrayBuffer.isView(e),becomes:"an object keyed by index"},{what:"a function",is:e=>typeof e=="function",becomes:"nothing at all"},{what:"undefined",is:e=>e===void 0,becomes:"nothing at all"}],Er=6;function An(e){let t=65536,n=!1,o=Ir,r=!1,s=_(e.onError);function a(i){let m=o;m.known&&Pr(m.stored,i)||(o=Object.freeze({known:!0,stored:i===null?null:Object.freeze({...i})}),s.emit(o,m))}let u;function p(i,m){if(r=m==="dev",typeof i!="object"||i===null)return;n=!0;let f=i;typeof f.bytes=="number"&&Number.isFinite(f.bytes)&&(t=Math.max(65536,f.bytes));let l=f.stored===null?null:Ee(f.stored);(f.stored===null||l!==null)&&(o=Object.freeze({known:!0,stored:l===null?null:Object.freeze(l)}))}function d(i){if(typeof i=="string")return[i,"text"];let m;try{m=JSON.stringify(i)}catch(f){throw new R({code:"bad_params",message:`That state cannot be saved: ${f instanceof Error?f.message:String(f)}`})}if(m===void 0)throw new R({code:"bad_params",message:"That state cannot be saved: it is not a JSON value."});return[m,"json"]}function c(i){for(let m of vt(i,Er,new Set))e.onError(new Error(`save: ${m.at} is ${m.what}, which JSON turns into ${m.becomes}.`))}return{adopt:p,save:{get available(){return n},get status(){return o},on:s.on,get maxBytes(){return t},async read(){let i=await e.peer().call("save.read",{}),m=st(i?.record);if(m===null)return a(null),null;if(u=m.rev,a(Ee(i?.stored)??{at:o.known&&o.stored!==null?o.stored.at:Date.now(),version:m.version,bytes:ke(m.state)}),m.encoding==="text")return{version:m.version,state:m.state};try{return{version:m.version,state:JSON.parse(m.state)}}catch(f){return e.onError(new Error(`save: the stored state could not be parsed (${f instanceof Error?f.message:String(f)}).`)),null}},async write(i,m){let[f,l]=d(i);r&&c(i);let b=ke(f);if(b>t)throw new R({code:"too_large",message:`That save is ${b} bytes and this host takes ${t}.`});let v={version:m?.version??xr,encoding:l,state:f};u!==void 0&&(v.rev=u);let I=(await e.peer().call("save.write",v))?.rev;u=typeof I=="string"?I:void 0,a({at:Date.now(),version:v.version,bytes:b})},async clear(){await e.peer().call("save.clear",{}),u=void 0,a(null)}}}}var Ir=Object.freeze({known:!1});function Pr(e,t){return e===null||t===null?e===t:e.at===t.at&&e.version===t.version&&e.bytes===t.bytes}function vt(e,t,n,o="the state"){for(let s of kr)if(s.is(e))return[{at:o,what:s.what,becomes:s.becomes}];if(t<=0||typeof e!="object"||e===null)return[];if(n.has(e))return[];n.add(e);let r=[];if(Array.isArray(e))return e.forEach((s,a)=>{r.push(...vt(s,t-1,n,`${o}[${a}]`))}),r;for(let[s,a]of Object.entries(e))r.push(...vt(a,t-1,n,`${o}.${s}`));return r}function xn(e){let t=!1,n=[],o,r=kn,s=_(e.onError);function a(d){o=d;let c=r;r=wn(n,o),Mr(c,r)||s.emit(r,c)}function u(d){return d!==void 0?d:n.length===1?n[0]:""}return{score:{get available(){return t},get boards(){return n},get standing(){return r},on:s.on,async submit(d,c){if(!ce(d))throw new R({code:"bad_params",message:"A score must be a finite number that a board can hold."});let i=c?.board,m=await e.peer().call("score.submit",i===void 0?{value:d}:{board:i,value:d}),f=ut(m)??{improved:!1,best:d},l=u(i);if(l!==""&&o!==void 0){let b=o??{},v=b[l];a({...b,[l]:{best:f.best,at:f.improved||v===void 0?Date.now():v.at,runs:(v?.runs??0)+1,...f.rank===void 0?{}:{rank:f.rank},...f.of===void 0?{}:{of:f.of}}})}return f}},adopt(d){let c=Ie(d);t=c!==null,n=c?.boards??[],o=c?.standing,r=wn(n,o)},onChange(d){let c=ct(d);c!==null&&a(c.standing)}}}var kn=Object.freeze({known:!1});function wn(e,t){if(t===void 0)return kn;let n={};for(let o of e)n[o]=t?.[o]??null;for(let[o,r]of Object.entries(t??{}))n[o]=r;return Object.freeze({known:!0,boards:Object.freeze(n)})}function Mr(e,t){if(!e.known||!t.known)return e.known===t.known;let n=Object.keys(e.boards);return n.length!==Object.keys(t.boards).length?!1:n.every(o=>{let r=e.boards[o],s=t.boards[o];return r==null||s===null||s===void 0?r===s:r.best===s.best&&r.at===s.at&&r.runs===s.runs&&r.rank===s.rank&&r.of===s.of})}function En(e){let t=null,n=!1;return{share:{get available(){return n},get data(){return t},offer(r){let s=r?.data,a=r?.meta;if(s!==void 0&&typeof s!="string"){e.onError(new R({code:"bad_params",message:"Share data must be a string: encode it however this game reads it back."}));return}if(a!==void 0&&ft(a)===null){e.onError(new R({code:"bad_params",message:"Share meta must be an object JSON can carry."}));return}W(()=>e.peer().call("share.offer",{...s===void 0?{}:{data:s},...a===void 0?{}:{meta:a}}),e.onError)}},adopt(r){let s=dt(r);t=s?.data??null,n=s?.offers===!0}}}function he(e){if(typeof e!="object"||e===null)return!1;let t=e;return t.v!==1?!1:typeof t.send=="function"&&typeof t.receive=="function"}function Ce(e){let t=e[F];return he(t)?t:null}function be(e){let t=null;return{send(n){e.send(JSON.stringify(n))},receive(n){return t=e.receive(o=>{let r;try{r=JSON.parse(o)}catch{return}oe(r)&&n(r)}),()=>{t?.(),t=null}},close(){}}}var At=2e3;function Oe(e={}){let t=e.scope??window,n=t,o=e.onError??(a=>console.error(a)),r=Ce(t);if(r)return Promise.resolve(be(r));if(t.parent!==t)return Promise.resolve(Lr(t,t.parent));let s=e.shimWaitMs??At;return new Promise((a,u)=>{let p=!1,d=m=>{if(!he(m)){o(new Error(`${U} was called with something that is not a v1 shim.`));return}if(p){o(new Error(`${U} was called twice; this game already has a channel.`));return}p=!0,clearTimeout(c),i(),a(be(m))},c=setTimeout(()=>{i(),u(new Error(`No host: there is no ${F} on the global and no parent frame to talk to. A Hopple game runs inside a Hopple host rather than on its own: while developing, run it with \`npx hopple dev\`. A native shell installs a shim before the game's bundle runs, or calls ${U} afterwards; neither happened within ${s}ms.`))},s);n[U]=d;function i(){n[U]===d&&delete n[U]}})}function Lr(e,t){return{send(n){t.postMessage(n,"*")},receive(n){let o=r=>{r.source===t&&oe(r.data)&&n(r.data)};return e.addEventListener("message",o),()=>e.removeEventListener("message",o)},close(){}}}async function Pn(e={}){let t=e.onError??(C=>console.error(C)),n=en(Cr(e.scope)),o=C=>{n.report("sdk",C),t(C)},r;try{r=e.transport??await Oe({scope:e.scope,shimWaitMs:e.shimWaitMs,onError:o})}catch(C){throw n.dispose(),C}let s=Dr(e.scope),a=()=>P,{lifecycle:u,onDispose:p}=mn({peer:a,onError:o}),d=yn({frames:Or(s),onError:o}),c=vn({onError:o}),i=fn({root:s?.document.documentElement??null,onError:o}),m=Yt({peer:a,onError:o,onRefused:t}),f=An({peer:a,onError:o}),l=xn({peer:a,onError:o}),b=Zt({peer:a,onError:o}),v=En({peer:a,onError:o}),M=Qt({scope:s,onError:o}),I=hn({peer:a,onError:o}),A=Sn({peer:a,onError:o}),x=()=>ge(i.layout.obstructions.filter(C=>C.kind!=="controls"),i.layout.frame),y=pn({scope:s,frame:()=>i.layout.frame,insets:x,setDrawn:i.setDrawn,onError:o}),E={frame:i.layout.frame,insets:x()};i.layout.on(()=>{let C={frame:i.layout.frame,insets:x()};C.frame.width===E.frame.width&&C.frame.height===E.frame.height&&Nr.every(G=>C.insets[G]===E.insets[G])||(E=C,y.relayout())});let k=mt({transport:r,handlers:{"system.ping":C=>({nonce:C.nonce}),"audio.allow":M.onAllow,"loop.allow":d.onAllow,"resources.allow":c.onAllow,"lifecycle.dispose":()=>(d.dispose(),p()),"audio.set":M.onSet,"pause.set":A.onSet},timeoutMs:e.timeoutMs,onError:o}),P=Kt(k),g=Math.max(e.timeoutMs??In,In),w;try{w=await k.call("system.hello",{protocol:1,sdk:qt.version},{timeoutMs:g})}catch(C){throw k.dispose(),n.dispose(),Tr(C,g)}k.useSession(w.peer),n.connect(k);let O=Ve(w.allowed);d.adopt(O.loop),c.adopt(O.resources),i.adopt(w.layout),k.on("layout.change",i.onChange),k.on("analytics.refused",m.refused),k.on("input.change",y.onChange),k.on("panel.change",I.onChange),k.on("achievement.change",b.onChange),k.on("score.change",l.onChange),d.loop.on(C=>{C||y.release()});let T=nn(w.info);return f.adopt(w.save,T.app.surface),l.adopt(w.score),b.adopt(w.achievements),v.adopt(w.share),M.adopt(w.audio,O.audio),I.adopt(w.panel),y.adopt(w.input),await _r(s,()=>i.layout.frame,o),{lifecycle:u,loop:d.loop,resources:c.resources,layout:i.layout,info:T,analytics:m.analytics,save:f.save,score:l.score,achievements:b.achievements,share:v.share,panel:I.panel,pause:A.pause,input:y.input,audio:M.audio,ping:()=>jr(k),dispose:()=>{d.dispose(),I.dispose(),y.dispose(),M.dispose(),i.dispose(),n.dispose(),k.dispose()}}}var Rr=1e3;async function _r(e,t,n){if(e===null)return;let o=()=>{let{width:r,height:s}=t();return r<=0||s<=0?!0:Math.abs(e.innerWidth-r)<=1&&Math.abs(e.innerHeight-s)<=1};o()||await new Promise(r=>{let s=()=>{o()&&u()},a=setTimeout(()=>{let{width:p,height:d}=t();n(new Error(`layout: the host says the frame is ${p}\xD7${d} but the window is ${e.innerWidth}\xD7${e.innerHeight}.`)),u()},Rr);function u(){clearTimeout(a),e?.removeEventListener("resize",s),r()}e.addEventListener("resize",s)})}function Cr(e){let t=e??(typeof window>"u"?void 0:window);return t===void 0?null:typeof t.addEventListener=="function"?t:null}function Or(e){let t=e??(typeof requestAnimationFrame=="function"?globalThis:null);return t!==null&&typeof t.requestAnimationFrame=="function"?{request:n=>t.requestAnimationFrame(n),cancel:n=>t.cancelAnimationFrame(n)}:{request:n=>setTimeout(()=>n(performance.now()),16),cancel:n=>clearTimeout(n)}}var In=1e4;function Tr(e,t){return!(e instanceof R)||e.code!=="timeout"?e:new R({code:"timeout",message:`No host answered: nothing replied to this game's hello within ${t}ms. A Hopple game runs inside a Hopple host, and whatever is holding this page is not one, or has stopped answering. While developing, run the game with \`npx hopple dev\`.`})}var Nr=["top","right","bottom","left"];function Dr(e){let t=e??(typeof window>"u"?void 0:window);if(t===void 0)return null;let n=t;return typeof n.addEventListener=="function"&&n.document!=null?t:null}async function jr(e){let t=$r(),n=performance.now(),o=await e.call("system.ping",{nonce:t}),r=performance.now()-n;if(o.nonce!==t)throw new Error(`The host answered a different ping: sent ${t}, got ${o.nonce}.`);return r}function $r(){return Math.random().toString(36).slice(2,10)}var Mn={type:"object",properties:{schema:{type:"number",const:1},id:{type:"string",pattern:"^[a-z0-9]+(?:-[a-z0-9]+)*$",maxLength:64,errorMessage:"must be lowercase letters, digits and hyphens"},version:{type:"string",pattern:"^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$",errorMessage:"must look like 1.0.0"},title:{type:"string",pattern:"\\S",maxLength:60,errorMessage:"must not be blank"},description:{type:"string",pattern:"\\S",maxLength:500,errorMessage:"must not be blank"},cover:{type:"string",pattern:"^(?!/)(?!.*\\.\\.)[A-Za-z0-9._/-]+\\.(?:png|jpe?g|webp)$",maxLength:200,errorMessage:'must be the path of a .png, .jpg or .webp file beside the config, like "cover.png"'},orientation:{$ref:"#/definitions/Orientation"},sdk:{type:"string",pattern:"^[\\^~]?\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$",errorMessage:"must be a version like 1.0.0, ^1.0.0 or ~1.0.0"},layout:{$ref:"#/definitions/LayoutDeclaration"},analytics:{$ref:"#/definitions/AnalyticsDeclaration"},save:{$ref:"#/definitions/SaveDeclaration"},score:{$ref:"#/definitions/ScoreDeclaration"},achievements:{$ref:"#/definitions/AchievementDeclaration"},input:{$ref:"#/definitions/InputDeclaration"},audio:{anyOf:[{$ref:"#/definitions/AudioDeclaration"},{type:"null"}]}},required:["schema","id","version","title","orientation","sdk","audio"],additionalProperties:{}};var Ln={type:"object",properties:{target:{type:"number",minimum:-9007199254740991,maximum:9007199254740991},direction:{$ref:"#/definitions/ScoreDirection"},format:{$ref:"#/definitions/ScoreFormat"},tiers:{type:"array",items:{$ref:"#/definitions/AchievementTier"},maxItems:8}},required:["target"],additionalProperties:{}};var Rn={type:"string",pattern:"^(?:key\\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|pointer\\.(?:primary|secondary)|mouse\\.(?:left|middle|right)|touch\\.tap|controls\\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\\.(?:north|south|east|west|start|select)|pad\\.(?:left|right)\\.(?:north|south|east|west|bumper|trigger))$",errorMessage:"is not a binding this SDK knows (a thumbstick is read rather than bound, and Escape is the host's key)"},_n={type:"object",properties:{left:{$ref:"#/definitions/ControlZoneSlots"},right:{$ref:"#/definitions/ControlZoneSlots"},full:{$ref:"#/definitions/ControlZoneSlots"}},additionalProperties:{}};var Cn={type:"string",pattern:"^(?:controls\\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\\.stick|pad\\.(?:left|right)\\.stick)$",errorMessage:"must name a thumbstick, such as pad.left.stick"};var wt="game.manifest.json",zr=1024*1024;var xt=1,Gr=Number.MAX_SAFE_INTEGER;var Br=Ln.properties.target.maximum;var Hr=Mn.required,Wr=Object.keys(_n.properties),xi=new RegExp(Rn.pattern),ki=new RegExp(Cn.pattern);return Gn(Fr);})();
|
|
154
|
+
`;function dr(e){let t=e.document.activeElement;if(t===null)return!1;if(t.isContentEditable)return!0;let n=t.tagName;return n==="INPUT"||n==="TEXTAREA"||n==="SELECT"}function an(e){let{scope:t,store:n}=e,o=t.document,r=[],s=[],a=!1,p=null,u=new Map,d=new Set,c=!0;function i(y,k,w,E,h=!1){let x=_=>{try{w(_)}catch(T){e.onError(T)}};y.addEventListener(k,x,h),E.push(()=>y.removeEventListener(k,x,h))}function m(y,k){let w=y;if(e.suspended()||dr(t))return;let E=`key.${w.code}`;e.isBound(E)&&(w.preventDefault(),e.onScheme("keyboard"),n.press(E,k))}function f(y){return y.pointerType!=="mouse"?"touch.tap":y.button===0?"mouse.left":y.button===1?"mouse.middle":y.button===2?"mouse.right":null}function l(y,k){let w=y;if(e.fromControls(y))return;if(e.onScheme(w.pointerType==="mouse"?"mouse":"touch"),k==="move"){n.movePointer({x:w.clientX,y:w.clientY,down:n.pointer?.down??!1});return}let E=k==="down";if(n.movePointer({x:w.clientX,y:w.clientY,down:E}),e.suspended())return;let h=f(w);h!==null&&n.press(h,E),(h==="touch.tap"||h==="mouse.left")&&n.press("pointer.primary",E)}function b(y,k){let w=e.deadzone(),E=Math.hypot(y,k);if(E<=w)return{x:0,y:0};if(w<=0||E===0)return{x:y,y:k};let h=(E-w)/(1-w)/E;return{x:y*h,y:k*h}}function v(){for(let y of t.navigator.getGamepads?.()??[])if(y!==null){if(y.mapping==="standard"){let k=u.get(y.index)??[],w=[];for(let[E,h]of y.buttons.entries()){let x=h.pressed;if(w[E]=x,x===(k[E]??!1)||!c)continue;let _=ee[E];_!=null&&(e.onScheme("gamepad"),e.suspended()||n.press(_,x))}u.set(y.index,w)}for(let[k,w]of gt.entries()){let E=c?b(y.axes[k*2]??0,y.axes[k*2+1]??0):{x:0,y:0};(E.x!==0||E.y!==0)&&e.onScheme("gamepad"),n.moveStick(w,E.x,E.y)}}}function P(){let y=()=>{p=null;try{v()}catch(k){e.onError(k)}d.size>0&&p===null&&P()};p=typeof t.requestAnimationFrame=="function"?t.requestAnimationFrame(y):setTimeout(y,16)}function I(){p!==null&&(typeof t.cancelAnimationFrame=="function"?t.cancelAnimationFrame(p):clearTimeout(p),p=null)}function O(y){d.add(y),p===null&&P()}function A(y){d.delete(y);let k=u.get(y)??[];u.delete(y);for(let[w,E]of k.entries()){let h=ee[w];E&&h!==null&&h!==void 0&&n.press(h,!1)}for(let w of gt)n.moveStick(w,0,0);d.size===0&&I()}return{attach(){i(t,"keydown",y=>m(y,!0),r),i(t,"keyup",y=>m(y,!1),r),i(t,"pointerdown",y=>l(y,"down"),r),i(t,"pointerup",y=>l(y,"up"),r),i(t,"pointercancel",y=>l(y,"up"),r),i(t,"blur",()=>{c=!1,n.releaseAll()},r),i(t,"focus",()=>{c=!0},r),i(o,"visibilitychange",()=>{o.visibilityState==="hidden"&&n.releaseAll()},r),i(t,"contextmenu",y=>{e.isBound("mouse.right")&&y.preventDefault()},r),i(t,"gamepadconnected",y=>{O(y.gamepad.index)},r),i(t,"gamepaddisconnected",y=>{A(y.gamepad.index)},r);for(let y of t.navigator.getGamepads?.()??[])y!==null&&O(y.index)},watchPointer(y){if(y!==a){if(a=y,y){i(t,"pointermove",k=>l(k,"move"),s),i(o,"pointerleave",()=>n.movePointer(null),s);return}for(let k of s.splice(0))k()}},detach(){this.watchPointer(!1);for(let y of r.splice(0))y();I(),d.clear(),u.clear()}}}var cn={x:0,y:0};function un(e){let t={},n=new Map,o=new Set,r=new Set,s=new Map,a=null,p=new Map,u=new Map,d=R(e.onError);function c(f,l){let b=f.get(l);return b===void 0&&(b=R(e.onError),f.set(l,b)),b}function i(f,l){(l?o:r).add(f),p.get(f)?.emit(l,f)}function m(f,l,b){let v=n.get(f);v===void 0&&(v=new Set,n.set(f,v));let P=v.size>0;b?v.add(l):v.delete(l);let I=v.size>0;P!==I&&i(f,I)}return{setMapping(f,l){t=f,l?.release!==!1&&this.releaseAll()},get mapping(){return t},press(f,l,b){for(let[v,P]of Object.entries(t))P.bind.some(I=>Me(I,f))&&m(v,b??f,l)},pressAction(f,l,b){m(f,l,b)},moveStick(f,l,b){let v=s.get(f);if(v!==void 0&&v.x===l&&v.y===b)return;let P={x:l,y:b};s.set(f,P);for(let[I,O]of u)Le(I,f)&&O.emit(P,f)},movePointer(f){f===null&&a===null||f!==null&&a!==null&&f.x===a.x&&f.y===a.y&&f.down===a.down||(a=f,d.emit(f))},releaseAll(){for(let[f,l]of n)l.size!==0&&(l.clear(),i(f,!1));for(let f of[...s.keys()])this.moveStick(f,0,0);this.movePointer(null)},releaseFrom(f){for(let[l,b]of n)if(b.size!==0){for(let v of[...b])f(v)&&b.delete(v);b.size===0&&i(l,!1)}},pressed(f){return(n.get(f)?.size??0)>0},stick(f){let l=s.get(f);if(l!==void 0)return l;for(let[b,v]of s)if(Le(f,b)&&(v.x!==0||v.y!==0))return v;return cn},get pointer(){return a},read(){let f=o,l=r;o=new Set,r=new Set;let b=new Set;for(let[I,O]of n)O.size>0&&b.add(I);let v=new Map(s),P=a===null?null:{...a};return Object.freeze({pressed:I=>b.has(I),started:I=>f.has(I),ended:I=>l.has(I),stick(I){let O=v.get(I);if(O!==void 0)return O;for(let[A,y]of v)if(Le(I,A)&&(y.x!==0||y.y!==0))return y;return cn},pointer:P})},onAction(f,l){return c(p,f).on(l)},onStick(f,l){return c(u,f).on(l)},onPointer(f){return d.on(f)},get watchingPointer(){return d.size>0},dispose(){p.clear(),u.clear(),d.clear(),n.clear(),s.clear(),a=null}}}var _e=.15;function pn(e){let t=e.scope,n={},o={},r=un({onError:e.onError}),s=Object.freeze({}),a={},p="keyboard",u="keyboard",d=!1,c="floating",i=!1,m=!1,f={scheme:u},l=null,b=!1,v=null,P=R(e.onError),I=!1,O=new Set;function A(S,g){let M=S==="action"?g in n||g in a:g in o;!I||M||O.has(`${S}:${g}`)||(O.add(`${S}:${g}`),e.onError(new Error(`input: the manifest declares no ${S} "${g}".`)))}function y(){if(t===null)return null;let S=t[me];return ye(S)?S:null}function k(S,g){let M=y();if(M===null)return g;try{let N=S(M);return N===void 0?g:N}catch(N){return e.onError(N),g}}function w(){let S=k(g=>g.deadzone,_e);return typeof S=="number"&&S>=0&&S<1?S:_e}function E(){let S={},g=!1;for(let[D,G]of Object.entries(n)){let De=a[D],je=De?.bind??G.bind,$e={bind:je},Et=De?.title??G.title;typeof Et=="string"&&($e.title=Et);let It=h(D,je,De);It!==""&&($e.label=It),S[D]=$e,dn(s[D]?.bind,je)||(g=!0)}for(let[D,G]of Object.entries(a))S[D]===void 0&&(S[D]=G,g=!0);for(let D of Object.keys(s))S[D]===void 0&&(g=!0);let M=s;s=Object.freeze(S),r.setMapping(S,{release:g});let N=f,$=N.scheme!==u||!fr(M,s);f={scheme:u},$&&P.emit(Object.freeze({mapping:s,scheme:u}),Object.freeze({mapping:M,scheme:N.scheme}))}function h(S,g,M){if(M?.label!==void 0&&p===u)return M.label;let N=de(g,u),$=N===void 0?"":fe(N,u);if($!=="")return $;let D=n[S]?.label;return typeof D=="string"?D:""}function x(S){S!==u&&(u=S,E())}let T=t===null?null:sn({scope:t,report:{press(S,g,M){let $=v?.zones.flatMap(G=>G.controls).find(G=>G.id===S)?.buttons[g];if($===void 0||(x("touch"),i))return;let D=`${ln}${S}.${g}`;r.pressAction($.action,D,M),$.as!==void 0&&r.press($.as,M,`${D}.as`),k(G=>G.haptic?.(M?"press":"release"),void 0)},move(S,g,M){let N=v?.zones.flatMap($=>$.controls).find($=>$.id===S);N!==void 0&&(x("touch"),!i&&(N.axis==="x"?r.moveStick(N.stick,g,0):N.axis==="y"?r.moveStick(N.stick,0,M):r.moveStick(N.stick,g,M)))}},shim:y,onError:e.onError}),j=t===null?null:an({scope:t,store:r,onScheme:x,isBound:C,suspended:()=>i,deadzone:w,fromControls:S=>T?.owns(S)??!1,onError:e.onError});function C(S){for(let g of Object.values(s))if(g.bind.some(M=>Me(M,S)))return!0;return!1}function U(){j?.watchPointer(m||r.watchingPointer)}function X(){let S=Object.keys(o);return S.length===1?S[0]:null}function F(){let S=e.frame(),g=e.insets(),M=l,N=M===null?void 0:o[M];if(!(T!==null&&M!==null&&N!==void 0&&d&&k(D=>D.decide?.({layout:M,frame:S,insets:g,contract:he}),!0)!==!1)||T===null||M===null||N===void 0){kt();return}if(v=on(M,N,n,S,g,c),T.render(v),!T.fits(S,g)){kt();return}b=!0,e.setDrawn(T.rects())}function kt(){!b&&v===null||(T?.clear(),v=null,b=!1,e.setDrawn([]))}function Ne(){r.releaseFrom(S=>S.startsWith(ln));for(let S of v?.zones??[])for(let g of S.controls)g.type==="thumbstick"&&r.moveStick(g.stick,0,0)}let On={show(S){let g=S??X();g!==null&&(A("layout",g),g!==l&&Ne(),l=g,F())},hide(){l!==null&&(Ne(),l=null,F())},get showing(){return b},get asked(){return l},get available(){return d}},Tn={onAction(S,g){return A("action",S),r.onAction(S,g)},onStick:(S,g)=>r.onStick(S,g),onPointer(S){let g=r.onPointer(S);return U(),()=>{g(),U()}},get mapping(){return s},on:P.on,get scheme(){return u},controls:On,get poll(){return m},set poll(S){m=S,U()},read(){let S=r.read();return Object.freeze({pressed:g=>(A("action",g),S.pressed(g)),started:g=>(A("action",g),S.started(g)),ended:g=>(A("action",g),S.ended(g)),stick:g=>S.stick(g),pointer:S.pointer})},suspend(){i||(i=!0,r.releaseAll())},resume(){i=!1},get suspended(){return i}};return E(),j?.attach(),{input:Tn,adopt(S){let g=Ze(S);g!==null&&(I=!0,n=g.actions,o=g.layouts,a=g.mapping,p=g.scheme,u=g.scheme,d=g.controls,c=g.sticks,E(),F())},onChange(S){let g=Ye(S);if(g===null)return;a=g.mapping,p=g.scheme,u=g.scheme;let M={available:d,sticks:c};d=g.controls,c=g.sticks,E(),M.sticks!==c&&Ne(),(M.available!==d||M.sticks!==c)&&F()},relayout(){F()},release:()=>r.releaseAll(),dispose(){j?.detach(),T?.dispose(),r.dispose(),P.clear()}}}var ln="controls.";function dn(e,t){return e===void 0?!1:e.length===t.length&&e.every((n,o)=>n===t[o])}function fr(e,t){let n=Object.keys(e);return n.length!==Object.keys(t).length?!1:n.every(o=>{let r=e[o],s=t[o];return r===void 0||s===void 0?!1:r.title===s.title&&r.label===s.label&&dn(r.bind,s.bind)})}var mr={top:0,right:0,bottom:0,left:0},yr=["top","right","bottom","left"],bt={width:0,height:0};function fn(e){let t=bt,n=[],o=[],r=[],s=null,a=R(e.onError);function p(){return s??(s=ge(r,t)),s}function u(){return{frame:t,obstructions:r,insets:p()}}function d(){if(e.root===null)return;let m=p();for(let f of yr)e.root.style.setProperty(`--hopple-inset-${f}`,`${m[f]}px`)}function c(m){let f=u(),l=Sr(m);t=l.frame,n=l.obstructions,i(f)}function i(m){if(r=o.length===0?n:[...n,...o],s=null,d(),m.frame.width===t.width&&m.frame.height===t.height&&br(m.obstructions,r))return;let f=u();a.emit(f,m)}return{adopt:c,onChange:c,setDrawn(m){let f=u();o=m.map(l=>({rect:l,kind:"controls",reserved:!0})),i(f)},dispose(){a.clear()},layout:{get frame(){return t},get insets(){return p()},get obstructions(){return r},on:a.on}}}function ge(e,t,n={}){let o={...mr};if(t.width<=0||t.height<=0)return o;let r=n.only==="strips";for(let s of e){if(!s.reserved)continue;let a=gr(s.rect,t);if(a===null||r&&!hr(a,t))continue;let p=[["top",a.y+a.height,t.width],["bottom",t.height-a.y,t.width],["left",a.x+a.width,t.height],["right",t.width-a.x,t.height]],u=p[0];for(let d of p)d[1]*d[2]<u[1]*u[2]&&(u=d);o[u[0]]=Math.max(o[u[0]],u[1])}return o}var Ce=1;function hr(e,t){return e.x<=Ce&&e.x+e.width>=t.width-Ce||e.y<=Ce&&e.y+e.height>=t.height-Ce}function gr(e,t){let n=Math.max(0,e.x),o=Math.max(0,e.y),r=Math.min(t.width,e.x+e.width),s=Math.min(t.height,e.y+e.height);return r<=n||s<=o?null:{x:n,y:o,width:r-n,height:s-o}}function br(e,t){return e===t?!0:e.length!==t.length?!1:e.every((n,o)=>{let r=t[o];return r!==void 0&&n.kind===r.kind&&n.reserved===r.reserved&&n.rect.x===r.rect.x&&n.rect.y===r.rect.y&&n.rect.width===r.rect.width&&n.rect.height===r.rect.height})}function Sr(e){let t=e;if(typeof t!="object"||t===null)return{frame:bt,obstructions:[]};let n=Ar(t.frame)?t.frame:bt;if(!Array.isArray(t.obstructions))return{frame:n,obstructions:[]};let o=[];for(let r of t.obstructions){if(typeof r!="object"||r===null)continue;let{rect:s,kind:a,reserved:p}=r;!vr(s)||typeof p!="boolean"||o.push({rect:s,reserved:p,kind:a??"chrome"})}return{frame:n,obstructions:o}}function vr(e){if(typeof e!="object"||e===null)return!1;let t=e;return te(t.x)&&te(t.y)&&te(t.width)&&te(t.height)}function Ar(e){if(typeof e!="object"||e===null)return!1;let t=e;return te(t.width)&&te(t.height)}function te(e){return typeof e=="number"&&Number.isFinite(e)}function mn(e){let t="loading",n=!1,o=R(e.onError);async function r(s){let a=t;t=s,await o.settle(s,a)}return{async onDispose(){return t!=="disposed"&&await r("disposed"),{}},lifecycle:{get phase(){return t},ready(){t!=="loading"||n||(n=!0,W(()=>e.peer().call("lifecycle.ready",{}),s=>{n=!1,e.onError(s)},()=>{t==="loading"&&r("ready")}))},on:o.on}}}var St=.1;function yn(e){let t=!0,n=!1,o=!1,r=R(e.onError),s=new Set;function a(){return t&&!o}function p(i){i.stopped||i.handle!==0||!a()||(i.last=void 0,i.handle=e.frames.request(m=>d(i,m)))}function u(i){i.handle!==0&&(e.frames.cancel(i.handle),i.handle=0)}function d(i,m){i.handle=0;let f=i.last===void 0?0:Math.min(Math.max(m-i.last,0)/1e3,St);i.last=m;try{i.frame(f,m)}catch(l){e.onError(l)}i.handle===0&&!i.stopped&&a()&&(i.handle=e.frames.request(l=>d(i,l)))}function c(){for(let i of s)a()?p(i):u(i)}return{adopt(i){n||(t=i),c()},async onAllow(i){return n=!0,i.allowed!==t&&(t=i.allowed,c(),await r.settle(t)),{allowed:t}},dispose(){o=!0;for(let i of s)i.stopped=!0,u(i);s.clear()},loop:{get allowed(){return t},on:r.on,tick(i){let m={frame:i,handle:0,last:void 0,stopped:!1};return s.add(m),p(m),()=>{m.stopped=!0,u(m),s.delete(m)}}}}}var wr=Object.freeze({isOpen:!1,page:null});function gn(e){let t=[],n=wr,o=R(e.onError),r={get available(){return t.length>0},get pages(){return t},get isOpen(){return n.isOpen},get page(){return n.page},on:o.on,open(a){W(()=>e.peer().call("panel.open",{id:a}),e.onError)}};function s(a){if(a.isOpen===n.isOpen&&a.page===n.page)return;let p=n;n=Object.freeze({isOpen:a.isOpen,page:a.page}),o.emit(n,p)}return{panel:r,adopt(a){let p=et(a);t=(p?.ids??[]).filter(bn),n=Object.freeze({isOpen:p?.isOpen===!0,page:hn(p?.page)})},onChange(a){let p=tt(a);p!==null&&s({isOpen:p.isOpen,page:hn(p.page)})},dispose(){o.clear()}}}function bn(e){return ae.includes(e)}function hn(e){return typeof e=="string"&&bn(e)?e:null}function Sn(e){let t=ce,n=R(e.onError);async function o(s){await n.settle(s)}return{pause:{get state(){return t},set(s){s!==t&&(t=s,e.peer().emit("pause.change",{state:t}))},on:n.on},async onSet(s){let a=rt(s);return a!==null&&await o(a.paused),{state:t}}}}function vn(e){let t=!0,n=!1,o=R(e.onError);return{adopt(r){n||(t=r)},async onAllow(r){return n=!0,r.allowed!==t&&(t=r.allowed,await o.settle(t)),{allowed:t}},resources:{get allowed(){return t},on:o.on}}}var xr=1,kr=[{what:"a Map",is:e=>e instanceof Map,becomes:"{}"},{what:"a Set",is:e=>e instanceof Set,becomes:"{}"},{what:"a Date",is:e=>e instanceof Date,becomes:"a string"},{what:"a typed array",is:e=>ArrayBuffer.isView(e),becomes:"an object keyed by index"},{what:"a function",is:e=>typeof e=="function",becomes:"nothing at all"},{what:"undefined",is:e=>e===void 0,becomes:"nothing at all"}],Er=6;function An(e){let t=65536,n=!1,o=Ir,r=!1,s=R(e.onError);function a(i){let m=o;m.known&&Pr(m.stored,i)||(o=Object.freeze({known:!0,stored:i===null?null:Object.freeze({...i})}),s.emit(o,m))}let p;function u(i,m){if(r=m==="dev",typeof i!="object"||i===null)return;n=!0;let f=i;typeof f.bytes=="number"&&Number.isFinite(f.bytes)&&(t=Math.max(65536,f.bytes));let l=f.stored===null?null:Ie(f.stored);(f.stored===null||l!==null)&&(o=Object.freeze({known:!0,stored:l===null?null:Object.freeze(l)}))}function d(i){if(typeof i=="string")return[i,"text"];let m;try{m=JSON.stringify(i)}catch(f){throw new L({code:"bad_params",message:`That state cannot be saved: ${f instanceof Error?f.message:String(f)}`})}if(m===void 0)throw new L({code:"bad_params",message:"That state cannot be saved: it is not a JSON value."});return[m,"json"]}function c(i){for(let m of vt(i,Er,new Set))e.onError(new Error(`save: ${m.at} is ${m.what}, which JSON turns into ${m.becomes}.`))}return{adopt:u,save:{get available(){return n},get status(){return o},on:s.on,get maxBytes(){return t},async read(){let i=await e.peer().call("save.read",{}),m=st(i?.record);if(m===null)return a(null),null;if(p=m.rev,a(Ie(i?.stored)??{at:o.known&&o.stored!==null?o.stored.at:Date.now(),version:m.version,bytes:Ee(m.state)}),m.encoding==="text")return{version:m.version,state:m.state};try{return{version:m.version,state:JSON.parse(m.state)}}catch(f){return e.onError(new Error(`save: the stored state could not be parsed (${f instanceof Error?f.message:String(f)}).`)),null}},async write(i,m){let[f,l]=d(i);r&&c(i);let b=Ee(f);if(b>t)throw new L({code:"too_large",message:`That save is ${b} bytes and this host takes ${t}.`});let v={version:m?.version??xr,encoding:l,state:f};p!==void 0&&(v.rev=p);let I=(await e.peer().call("save.write",v))?.rev;p=typeof I=="string"?I:void 0,a({at:Date.now(),version:v.version,bytes:b})},async clear(){await e.peer().call("save.clear",{}),p=void 0,a(null)}}}}var Ir=Object.freeze({known:!1});function Pr(e,t){return e===null||t===null?e===t:e.at===t.at&&e.version===t.version&&e.bytes===t.bytes}function vt(e,t,n,o="the state"){for(let s of kr)if(s.is(e))return[{at:o,what:s.what,becomes:s.becomes}];if(t<=0||typeof e!="object"||e===null)return[];if(n.has(e))return[];n.add(e);let r=[];if(Array.isArray(e))return e.forEach((s,a)=>{r.push(...vt(s,t-1,n,`${o}[${a}]`))}),r;for(let[s,a]of Object.entries(e))r.push(...vt(a,t-1,n,`${o}.${s}`));return r}function xn(e){let t=!1,n=[],o,r=kn,s=R(e.onError);function a(d){o=d;let c=r;r=wn(n,o),Mr(c,r)||s.emit(r,c)}function p(d){return d!==void 0?d:n.length===1?n[0]:""}return{score:{get available(){return t},get boards(){return n},get standing(){return r},on:s.on,async submit(d,c){if(!ue(d))throw new L({code:"bad_params",message:"A score must be a finite number that a board can hold."});let i=c?.board,m=await e.peer().call("score.submit",i===void 0?{value:d}:{board:i,value:d}),f=ut(m)??{improved:!1,best:d},l=p(i);if(l!==""&&o!==void 0){let b=o??{},v=b[l];a({...b,[l]:{best:f.best,at:f.improved||v===void 0?Date.now():v.at,runs:(v?.runs??0)+1,...f.rank===void 0?{}:{rank:f.rank},...f.of===void 0?{}:{of:f.of}}})}return f}},adopt(d){let c=Pe(d);t=c!==null,n=c?.boards??[],o=c?.standing,r=wn(n,o)},onChange(d){let c=ct(d);c!==null&&a(c.standing)}}}var kn=Object.freeze({known:!1});function wn(e,t){if(t===void 0)return kn;let n={};for(let o of e)n[o]=t?.[o]??null;for(let[o,r]of Object.entries(t??{}))n[o]=r;return Object.freeze({known:!0,boards:Object.freeze(n)})}function Mr(e,t){if(!e.known||!t.known)return e.known===t.known;let n=Object.keys(e.boards);return n.length!==Object.keys(t.boards).length?!1:n.every(o=>{let r=e.boards[o],s=t.boards[o];return r==null||s===null||s===void 0?r===s:r.best===s.best&&r.at===s.at&&r.runs===s.runs&&r.rank===s.rank&&r.of===s.of})}function En(e){let t=null,n=!1;return{share:{get available(){return n},get data(){return t},offer(r){let s=r?.data,a=r?.meta;if(s!==void 0&&typeof s!="string"){e.onError(new L({code:"bad_params",message:"Share data must be a string: encode it however this game reads it back."}));return}if(a!==void 0&&ft(a)===null){e.onError(new L({code:"bad_params",message:"Share meta must be an object JSON can carry."}));return}W(()=>e.peer().call("share.offer",{...s===void 0?{}:{data:s},...a===void 0?{}:{meta:a}}),e.onError)}},adopt(r){let s=dt(r);t=s?.data??null,n=s?.offers===!0}}}function be(e){if(typeof e!="object"||e===null)return!1;let t=e;return t.v!==1?!1:typeof t.send=="function"&&typeof t.receive=="function"}function Oe(e){let t=e[V];return be(t)?t:null}function Se(e){let t=null;return{send(n){e.send(JSON.stringify(n))},receive(n){return t=e.receive(o=>{let r;try{r=JSON.parse(o)}catch{return}se(r)&&n(r)}),()=>{t?.(),t=null}},close(){}}}var At=2e3;function Te(e={}){let t=e.scope??window,n=t,o=e.onError??(a=>console.error(a)),r=Oe(t);if(r)return Promise.resolve(Se(r));if(t.parent!==t)return Promise.resolve(Lr(t,t.parent));let s=e.shimWaitMs??At;return new Promise((a,p)=>{let u=!1,d=m=>{if(!be(m)){o(new Error(`${z} was called with something that is not a v1 shim.`));return}if(u){o(new Error(`${z} was called twice; this game already has a channel.`));return}u=!0,clearTimeout(c),i(),a(Se(m))},c=setTimeout(()=>{i(),p(new Error(`No host: there is no ${V} on the global and no parent frame to talk to. A Hopple game runs inside a Hopple host rather than on its own: while developing, run it with \`npx hopple dev\`. A native shell installs a shim before the game's bundle runs, or calls ${z} afterwards; neither happened within ${s}ms.`))},s);n[z]=d;function i(){n[z]===d&&delete n[z]}})}function Lr(e,t){return{send(n){t.postMessage(n,"*")},receive(n){let o=r=>{r.source===t&&se(r.data)&&n(r.data)};return e.addEventListener("message",o),()=>e.removeEventListener("message",o)},close(){}}}async function Pn(e={}){let t=e.onError??(C=>console.error(C)),n=en(Cr(e.scope)),o=C=>{n.report("sdk",C),t(C)},r;try{r=e.transport??await Te({scope:e.scope,shimWaitMs:e.shimWaitMs,onError:o})}catch(C){throw n.dispose(),C}let s=Dr(e.scope),a=()=>E,{lifecycle:p,onDispose:u}=mn({peer:a,onError:o}),d=yn({frames:Or(s),onError:o}),c=vn({onError:o}),i=fn({root:s?.document.documentElement??null,onError:o}),m=Yt({peer:a,onError:o,onRefused:t}),f=An({peer:a,onError:o}),l=xn({peer:a,onError:o}),b=Zt({peer:a,onError:o}),v=En({peer:a,onError:o}),P=Qt({scope:s,onError:o}),I=gn({peer:a,onError:o}),O=Sn({peer:a,onError:o}),A=()=>ge(i.layout.obstructions.filter(C=>C.kind!=="controls"),i.layout.frame),y=pn({scope:s,frame:()=>i.layout.frame,insets:A,setDrawn:i.setDrawn,onError:o}),k={frame:i.layout.frame,insets:A()};i.layout.on(()=>{let C={frame:i.layout.frame,insets:A()};C.frame.width===k.frame.width&&C.frame.height===k.frame.height&&Nr.every(U=>C.insets[U]===k.insets[U])||(k=C,y.relayout())});let w=mt({transport:r,handlers:{"system.ping":C=>({nonce:C.nonce}),"audio.allow":P.onAllow,"loop.allow":d.onAllow,"resources.allow":c.onAllow,"lifecycle.dispose":()=>(d.dispose(),u()),"audio.set":P.onSet,"pause.set":O.onSet},timeoutMs:e.timeoutMs,onError:o}),E=Kt(w),h=Math.max(e.timeoutMs??In,In),x;try{x=await w.call("system.hello",{protocol:1,sdk:qt.version},{timeoutMs:h})}catch(C){throw w.dispose(),n.dispose(),Tr(C,h)}w.useSession(x.peer),n.connect(w);let _=Ve(x.allowed);d.adopt(_.loop),c.adopt(_.resources),i.adopt(x.layout),w.on("layout.change",i.onChange),w.on("analytics.refused",m.refused),w.on("input.change",y.onChange),w.on("panel.change",I.onChange),w.on("achievement.change",b.onChange),w.on("score.change",l.onChange),d.loop.on(C=>{C||y.release()});let T=nn(x.info);return f.adopt(x.save,T.app.surface),l.adopt(x.score),b.adopt(x.achievements),v.adopt(x.share),P.adopt(x.audio,_.audio),I.adopt(x.panel),y.adopt(x.input),await _r(s,()=>i.layout.frame,o),{lifecycle:p,loop:d.loop,resources:c.resources,layout:i.layout,info:T,analytics:m.analytics,save:f.save,score:l.score,achievements:b.achievements,share:v.share,panel:I.panel,pause:O.pause,input:y.input,audio:P.audio,ping:()=>jr(w),dispose:()=>{d.dispose(),I.dispose(),y.dispose(),P.dispose(),i.dispose(),n.dispose(),w.dispose()}}}var Rr=1e3;async function _r(e,t,n){if(e===null)return;let o=()=>{let{width:r,height:s}=t();return r<=0||s<=0?!0:Math.abs(e.innerWidth-r)<=1&&Math.abs(e.innerHeight-s)<=1};o()||await new Promise(r=>{let s=()=>{o()&&p()},a=setTimeout(()=>{let{width:u,height:d}=t();n(new Error(`layout: the host says the frame is ${u}\xD7${d} but the window is ${e.innerWidth}\xD7${e.innerHeight}.`)),p()},Rr);function p(){clearTimeout(a),e?.removeEventListener("resize",s),r()}e.addEventListener("resize",s)})}function Cr(e){let t=e??(typeof window>"u"?void 0:window);return t===void 0?null:typeof t.addEventListener=="function"?t:null}function Or(e){let t=e??(typeof requestAnimationFrame=="function"?globalThis:null);return t!==null&&typeof t.requestAnimationFrame=="function"?{request:n=>t.requestAnimationFrame(n),cancel:n=>t.cancelAnimationFrame(n)}:{request:n=>setTimeout(()=>n(performance.now()),16),cancel:n=>clearTimeout(n)}}var In=1e4;function Tr(e,t){return!(e instanceof L)||e.code!=="timeout"?e:new L({code:"timeout",message:`No host answered: nothing replied to this game's hello within ${t}ms. A Hopple game runs inside a Hopple host, and whatever is holding this page is not one, or has stopped answering. While developing, run the game with \`npx hopple dev\`.`})}var Nr=["top","right","bottom","left"];function Dr(e){let t=e??(typeof window>"u"?void 0:window);if(t===void 0)return null;let n=t;return typeof n.addEventListener=="function"&&n.document!=null?t:null}async function jr(e){let t=$r(),n=performance.now(),o=await e.call("system.ping",{nonce:t}),r=performance.now()-n;if(o.nonce!==t)throw new Error(`The host answered a different ping: sent ${t}, got ${o.nonce}.`);return r}function $r(){return Math.random().toString(36).slice(2,10)}var Mn={type:"object",properties:{schema:{type:"number",const:1},id:{type:"string",pattern:"^[a-z0-9]+(?:-[a-z0-9]+)*$",maxLength:64,errorMessage:"must be lowercase letters, digits and hyphens"},owner:{type:"string",pattern:"^[a-z0-9]+(?:-[a-z0-9]+)*$",maxLength:64,errorMessage:"must be a handle: lowercase letters, digits and hyphens"},version:{type:"string",pattern:"^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$",errorMessage:"must look like 1.0.0"},title:{type:"string",pattern:"\\S",maxLength:60,errorMessage:"must not be blank"},description:{type:"string",pattern:"\\S",maxLength:500,errorMessage:"must not be blank"},cover:{type:"string",pattern:"^(?!/)(?!.*\\.\\.)[A-Za-z0-9._/-]+\\.(?:png|jpe?g|webp)$",maxLength:200,errorMessage:'must be the path of a .png, .jpg or .webp file beside the config, like "cover.png"'},orientation:{$ref:"#/definitions/Orientation"},sdk:{type:"string",pattern:"^[\\^~]?\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$",errorMessage:"must be a version like 1.0.0, ^1.0.0 or ~1.0.0"},layout:{$ref:"#/definitions/LayoutDeclaration"},analytics:{$ref:"#/definitions/AnalyticsDeclaration"},save:{$ref:"#/definitions/SaveDeclaration"},score:{$ref:"#/definitions/ScoreDeclaration"},achievements:{$ref:"#/definitions/AchievementDeclaration"},input:{$ref:"#/definitions/InputDeclaration"},audio:{anyOf:[{$ref:"#/definitions/AudioDeclaration"},{type:"null"}]}},required:["schema","id","version","title","orientation","sdk","audio"],additionalProperties:{}};var Ln={type:"object",properties:{target:{type:"number",minimum:-9007199254740991,maximum:9007199254740991},direction:{$ref:"#/definitions/ScoreDirection"},format:{$ref:"#/definitions/ScoreFormat"},tiers:{type:"array",items:{$ref:"#/definitions/AchievementTier"},maxItems:8}},required:["target"],additionalProperties:{}};var Rn={type:"string",pattern:"^(?:key\\.(?!Escape$)(?=.{1,48}$)[A-Za-z][A-Za-z0-9]*|pointer\\.(?:primary|secondary)|mouse\\.(?:left|middle|right)|touch\\.tap|controls\\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\\.(?:north|south|east|west|start|select)|pad\\.(?:left|right)\\.(?:north|south|east|west|bumper|trigger))$",errorMessage:"is not a binding this SDK knows (a thumbstick is read rather than bound, and Escape is the host's key)"},_n={type:"object",properties:{left:{$ref:"#/definitions/ControlZoneSlots"},right:{$ref:"#/definitions/ControlZoneSlots"},full:{$ref:"#/definitions/ControlZoneSlots"}},additionalProperties:{}};var Cn={type:"string",pattern:"^(?:controls\\.(?=.{1,48}$)[a-z0-9]+(?:[_-][a-z0-9]+)*|pad\\.stick|pad\\.(?:left|right)\\.stick)$",errorMessage:"must name a thumbstick, such as pad.left.stick"};var wt="game.manifest.json",zr=1024*1024;var xt=1,Gr=Number.MAX_SAFE_INTEGER;var Br=Ln.properties.target.maximum;var Hr=Mn.required,Wr=Object.keys(_n.properties),xi=new RegExp(Rn.pattern),ki=new RegExp(Cn.pattern);return Gn(Fr);})();
|
|
139
155
|
/*! @hopple/sdk | Copyright (c) 2026 The Hopple authors | MIT License */
|
package/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
1
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/achievement.d.ts
|
|
2
2
|
/**
|
|
3
3
|
* The largest value progress will carry.
|
|
4
4
|
*
|
|
@@ -28,7 +28,7 @@ type AchievementResult = {
|
|
|
28
28
|
progress?: number;
|
|
29
29
|
};
|
|
30
30
|
//#endregion
|
|
31
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
31
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/analytics.d.ts
|
|
32
32
|
/**
|
|
33
33
|
* What a game says happened.
|
|
34
34
|
*
|
|
@@ -59,7 +59,7 @@ type AnalyticsValue = string | number | boolean;
|
|
|
59
59
|
/** @group Analytics */
|
|
60
60
|
type AnalyticsProps = Record<string, AnalyticsValue>;
|
|
61
61
|
//#endregion
|
|
62
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
62
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/audio.d.ts
|
|
63
63
|
/**
|
|
64
64
|
* How loud the game is allowed to be, which is the host's to decide and the
|
|
65
65
|
* game's to obey.
|
|
@@ -164,7 +164,7 @@ declare const AUDIO_FULL: AudioState;
|
|
|
164
164
|
*/
|
|
165
165
|
declare function audioVolumeOf(state: AudioState, channel?: string): number;
|
|
166
166
|
//#endregion
|
|
167
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
167
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/errors.d.ts
|
|
168
168
|
/**
|
|
169
169
|
* How a call fails.
|
|
170
170
|
*
|
|
@@ -234,7 +234,7 @@ declare class BridgeError extends Error {
|
|
|
234
234
|
constructor(error: ProtocolError);
|
|
235
235
|
}
|
|
236
236
|
//#endregion
|
|
237
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
237
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/envelope.d.ts
|
|
238
238
|
/**
|
|
239
239
|
* `p` is the protocol version, carried on every message rather than only at the
|
|
240
240
|
* handshake, because a message that arrives without context still has to be
|
|
@@ -275,7 +275,7 @@ type Envelope = {
|
|
|
275
275
|
data: unknown;
|
|
276
276
|
};
|
|
277
277
|
//#endregion
|
|
278
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
278
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/input.d.ts
|
|
279
279
|
/**
|
|
280
280
|
* What the host says about how this game is being played.
|
|
281
281
|
*
|
|
@@ -361,10 +361,12 @@ type InputLayoutAxis = 'x' | 'y';
|
|
|
361
361
|
* How an on-screen thumbstick meets a thumb: a player's setting, the same in
|
|
362
362
|
* every game they open.
|
|
363
363
|
*
|
|
364
|
-
* `floating` is
|
|
364
|
+
* `floating` is marked where the layout puts it, as a sign that there is a
|
|
365
365
|
* stick, and comes to wherever a thumb lands on its side of the screen — so a
|
|
366
366
|
* player never has to find it, and the place they first touch is always the
|
|
367
|
-
* centre.
|
|
367
|
+
* centre. A thumb that goes past its rim pulls it along, so turning back is
|
|
368
|
+
* never further than the stick is wide. `fixed` stays where it is drawn and is
|
|
369
|
+
* moved only from there.
|
|
368
370
|
*
|
|
369
371
|
* @group Input
|
|
370
372
|
*/
|
|
@@ -384,7 +386,7 @@ type InputChangeCause =
|
|
|
384
386
|
/** A player changed how the on-screen controls behave. */
|
|
385
387
|
'setting';
|
|
386
388
|
//#endregion
|
|
387
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
389
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/panel.d.ts
|
|
388
390
|
/**
|
|
389
391
|
* The pages of the panel this version of the protocol has a name for.
|
|
390
392
|
*
|
|
@@ -414,7 +416,7 @@ type PageId =
|
|
|
414
416
|
/** @group Panels */
|
|
415
417
|
declare const PAGES: readonly PageId[];
|
|
416
418
|
//#endregion
|
|
417
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
419
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/pause.d.ts
|
|
418
420
|
/**
|
|
419
421
|
* Whether the game is holding still, which the host may ask for and only the
|
|
420
422
|
* game can answer.
|
|
@@ -487,7 +489,7 @@ declare const PAUSE_STATES: readonly PauseState[];
|
|
|
487
489
|
*/
|
|
488
490
|
declare const PAUSE_NONE: PauseState;
|
|
489
491
|
//#endregion
|
|
490
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
492
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/save.d.ts
|
|
491
493
|
/**
|
|
492
494
|
* The one thing a game may keep.
|
|
493
495
|
*
|
|
@@ -538,7 +540,7 @@ type SaveStored = {
|
|
|
538
540
|
bytes: number;
|
|
539
541
|
};
|
|
540
542
|
//#endregion
|
|
541
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
543
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/score.d.ts
|
|
542
544
|
/**
|
|
543
545
|
* What a player did, as one number.
|
|
544
546
|
*
|
|
@@ -614,7 +616,7 @@ type ScoreStanding = {
|
|
|
614
616
|
of?: number;
|
|
615
617
|
};
|
|
616
618
|
//#endregion
|
|
617
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
619
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/share.d.ts
|
|
618
620
|
/**
|
|
619
621
|
* A link to this game, offered to the player by the host.
|
|
620
622
|
*
|
|
@@ -713,7 +715,7 @@ type ShareMeta = {
|
|
|
713
715
|
[key: string]: ShareMetaValue;
|
|
714
716
|
};
|
|
715
717
|
//#endregion
|
|
716
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
718
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/methods.d.ts
|
|
717
719
|
/**
|
|
718
720
|
* How far a game has got with its own preparation.
|
|
719
721
|
*
|
|
@@ -893,7 +895,7 @@ type DeviceInfo = {
|
|
|
893
895
|
tier: DeviceTier;
|
|
894
896
|
};
|
|
895
897
|
//#endregion
|
|
896
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
898
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/listeners.d.ts
|
|
897
899
|
/**
|
|
898
900
|
* The one way anything here is listened to: `on(listener)` hands back the
|
|
899
901
|
* function that stops it.
|
|
@@ -922,7 +924,7 @@ type DeviceInfo = {
|
|
|
922
924
|
*/
|
|
923
925
|
type Unsubscribe = () => void;
|
|
924
926
|
//#endregion
|
|
925
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
927
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/protocol/src/transport.d.ts
|
|
926
928
|
/** @group Bridge */
|
|
927
929
|
type Transport = {
|
|
928
930
|
send(message: Envelope): void;
|
|
@@ -1020,7 +1022,7 @@ declare global {
|
|
|
1020
1022
|
var __HOPPLE_SET_SHIM__: ((shim: HoppleShim) => void) | undefined;
|
|
1021
1023
|
}
|
|
1022
1024
|
//#endregion
|
|
1023
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
1025
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/manifest/src/manifest.d.ts
|
|
1024
1026
|
/**
|
|
1025
1027
|
* What a game says about itself.
|
|
1026
1028
|
*
|
|
@@ -1757,6 +1759,17 @@ type GameManifest = {
|
|
|
1757
1759
|
* @errorMessage must be lowercase letters, digits and hyphens
|
|
1758
1760
|
*/
|
|
1759
1761
|
id: string;
|
|
1762
|
+
/**
|
|
1763
|
+
* The handle of the account the game is published under: the first half of
|
|
1764
|
+
* `owner/id`. Absent, it is whoever publishes it. Present, only that account
|
|
1765
|
+
* and the game's collaborators may publish it.
|
|
1766
|
+
*
|
|
1767
|
+
* @category Useful
|
|
1768
|
+
* @pattern ^[a-z0-9]+(?:-[a-z0-9]+)*$
|
|
1769
|
+
* @maxLength 64
|
|
1770
|
+
* @errorMessage must be a handle: lowercase letters, digits and hyphens
|
|
1771
|
+
*/
|
|
1772
|
+
owner?: string;
|
|
1760
1773
|
/**
|
|
1761
1774
|
* The game's own version, `MAJOR.MINOR.PATCH`. Ours to read, never to parse for meaning.
|
|
1762
1775
|
*
|
|
@@ -1906,7 +1919,7 @@ type HoppleConfig = {
|
|
|
1906
1919
|
manifest: GameManifest;
|
|
1907
1920
|
};
|
|
1908
1921
|
//#endregion
|
|
1909
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
1922
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/achievements.d.ts
|
|
1910
1923
|
/**
|
|
1911
1924
|
* Where this player stands on one achievement.
|
|
1912
1925
|
*
|
|
@@ -2000,7 +2013,7 @@ type Achievements<Id extends string = string, Measured extends string = Id> = {
|
|
|
2000
2013
|
progress(id: Measured, value: number): void;
|
|
2001
2014
|
};
|
|
2002
2015
|
//#endregion
|
|
2003
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2016
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/analytics.d.ts
|
|
2004
2017
|
/**
|
|
2005
2018
|
* @group Analytics
|
|
2006
2019
|
* @category Important
|
|
@@ -2017,7 +2030,7 @@ type Analytics<Events extends object = Record<string, AnalyticsProps>> = {
|
|
|
2017
2030
|
track<Event extends keyof Events & string>(name: Event, props?: Events[Event]): void;
|
|
2018
2031
|
};
|
|
2019
2032
|
//#endregion
|
|
2020
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2033
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/audio.d.ts
|
|
2021
2034
|
/**
|
|
2022
2035
|
* Something about the game's sound changed: the host's order, the player's
|
|
2023
2036
|
* mute, a fader. Read what you need — `volume(channel)`, `allowed` — and apply
|
|
@@ -2110,7 +2123,7 @@ type Audio<Channel extends string = string> = {
|
|
|
2110
2123
|
media(element: HTMLMediaElement, channel?: Channel): Unsubscribe;
|
|
2111
2124
|
};
|
|
2112
2125
|
//#endregion
|
|
2113
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2126
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/info.d.ts
|
|
2114
2127
|
/**
|
|
2115
2128
|
* What the host said, frozen.
|
|
2116
2129
|
*
|
|
@@ -2128,7 +2141,7 @@ type Info = Readonly<{
|
|
|
2128
2141
|
device: Readonly<DeviceInfo>;
|
|
2129
2142
|
}>;
|
|
2130
2143
|
//#endregion
|
|
2131
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2144
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/layout.d.ts
|
|
2132
2145
|
/**
|
|
2133
2146
|
* How much of each edge belongs to the host. CSS pixels.
|
|
2134
2147
|
*
|
|
@@ -2238,7 +2251,7 @@ type InsetPolicy = {
|
|
|
2238
2251
|
*/
|
|
2239
2252
|
declare function insetsFrom(obstructions: readonly Obstruction[], frame: Size, policy?: InsetPolicy): Insets;
|
|
2240
2253
|
//#endregion
|
|
2241
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2254
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/names.d.ts
|
|
2242
2255
|
/**
|
|
2243
2256
|
* The keys of a declared map, or `string` where the map is not a literal one —
|
|
2244
2257
|
* which is what a manifest typed as the wide `GameManifest` gives — and
|
|
@@ -2345,7 +2358,7 @@ type EventProps<M> = M extends {
|
|
|
2345
2358
|
*/
|
|
2346
2359
|
type StickName = 'pad.left.stick' | 'pad.right.stick' | 'pad.stick' | `controls.${string}`;
|
|
2347
2360
|
//#endregion
|
|
2348
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2361
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/input/state.d.ts
|
|
2349
2362
|
/**
|
|
2350
2363
|
* Where the pointer is, in the frame's own coordinates.
|
|
2351
2364
|
*
|
|
@@ -2406,7 +2419,7 @@ type InputSnapshot<Action extends string = string> = {
|
|
|
2406
2419
|
pointer: PointerState | null;
|
|
2407
2420
|
};
|
|
2408
2421
|
//#endregion
|
|
2409
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2422
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/input/controls.d.ts
|
|
2410
2423
|
/**
|
|
2411
2424
|
* The name a host leaves its input shim under.
|
|
2412
2425
|
*
|
|
@@ -2483,11 +2496,12 @@ type ControlModel = {
|
|
|
2483
2496
|
/**
|
|
2484
2497
|
* How a thumbstick meets a thumb, as the player set it on their host.
|
|
2485
2498
|
*
|
|
2486
|
-
* `floating`:
|
|
2487
|
-
* stick,
|
|
2488
|
-
* the place they first touch is the centre
|
|
2489
|
-
*
|
|
2490
|
-
* stick a game can be
|
|
2499
|
+
* `floating`: marked where the layout puts it, so a player can see there is
|
|
2500
|
+
* a stick, brought to wherever a thumb lands on its side of the screen, so
|
|
2501
|
+
* the place they first touch is the centre, and pulled along by a thumb that
|
|
2502
|
+
* goes past its rim. `fixed`: moved only from where it is drawn. A renderer
|
|
2503
|
+
* that ignores this draws `fixed`, which is still a stick a game can be
|
|
2504
|
+
* played with.
|
|
2491
2505
|
*/
|
|
2492
2506
|
sticks: InputSticks;
|
|
2493
2507
|
};
|
|
@@ -2585,7 +2599,7 @@ declare function isInputShim(value: unknown): value is InputShim;
|
|
|
2585
2599
|
*/
|
|
2586
2600
|
declare const INPUT_CONTRACT = 4;
|
|
2587
2601
|
//#endregion
|
|
2588
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2602
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/input/bindings.d.ts
|
|
2589
2603
|
/**
|
|
2590
2604
|
* The standard mapping's buttons, by index, as the names this SDK uses.
|
|
2591
2605
|
*
|
|
@@ -2637,7 +2651,7 @@ declare function servesScheme(binding: string, scheme: InputScheme): boolean;
|
|
|
2637
2651
|
*/
|
|
2638
2652
|
declare function labelFor(binding: string, scheme: InputScheme): string;
|
|
2639
2653
|
//#endregion
|
|
2640
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2654
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/input/index.d.ts
|
|
2641
2655
|
/**
|
|
2642
2656
|
* How far a stick moves before it counts, where no shim has an opinion.
|
|
2643
2657
|
*
|
|
@@ -2773,7 +2787,7 @@ type Controls<Layout extends string = string> = {
|
|
|
2773
2787
|
readonly available: boolean;
|
|
2774
2788
|
};
|
|
2775
2789
|
//#endregion
|
|
2776
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2790
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/lifecycle.d.ts
|
|
2777
2791
|
/** @group Lifecycle */
|
|
2778
2792
|
type LifecycleListener = (phase: Phase, previous: Phase) => void | Promise<void>;
|
|
2779
2793
|
/**
|
|
@@ -2792,7 +2806,7 @@ type Lifecycle = {
|
|
|
2792
2806
|
on(listener: LifecycleListener): Unsubscribe;
|
|
2793
2807
|
};
|
|
2794
2808
|
//#endregion
|
|
2795
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2809
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/loop.d.ts
|
|
2796
2810
|
/**
|
|
2797
2811
|
* The host has let the game run its loop, or stopped it. Return a promise to
|
|
2798
2812
|
* make the host wait.
|
|
@@ -2846,7 +2860,7 @@ type Loop = {
|
|
|
2846
2860
|
tick(frame: FrameCallback): Unsubscribe;
|
|
2847
2861
|
};
|
|
2848
2862
|
//#endregion
|
|
2849
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2863
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/panel.d.ts
|
|
2850
2864
|
/**
|
|
2851
2865
|
* Where the host's panel stands over the game.
|
|
2852
2866
|
*
|
|
@@ -2955,7 +2969,7 @@ type Panel = {
|
|
|
2955
2969
|
open(page: PageId): void;
|
|
2956
2970
|
};
|
|
2957
2971
|
//#endregion
|
|
2958
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
2972
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/pause.d.ts
|
|
2959
2973
|
/**
|
|
2960
2974
|
* The host has asked for a pause, or for the game to carry on.
|
|
2961
2975
|
*
|
|
@@ -3016,7 +3030,7 @@ type Pause = {
|
|
|
3016
3030
|
on(listener: PauseListener): Unsubscribe;
|
|
3017
3031
|
};
|
|
3018
3032
|
//#endregion
|
|
3019
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3033
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/resources.d.ts
|
|
3020
3034
|
/**
|
|
3021
3035
|
* The host has let the game hold its resources again, or asked for them back.
|
|
3022
3036
|
* Return a promise to make the host wait until they have gone, or come back.
|
|
@@ -3046,7 +3060,7 @@ type Resources = {
|
|
|
3046
3060
|
on(listener: ResourcesListener): Unsubscribe;
|
|
3047
3061
|
};
|
|
3048
3062
|
//#endregion
|
|
3049
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3063
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/save.d.ts
|
|
3050
3064
|
/**
|
|
3051
3065
|
* What a game gets back from a read: its own schema number, and its state.
|
|
3052
3066
|
*
|
|
@@ -3135,7 +3149,7 @@ type Save = {
|
|
|
3135
3149
|
readonly maxBytes: number;
|
|
3136
3150
|
};
|
|
3137
3151
|
//#endregion
|
|
3138
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3152
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/score.d.ts
|
|
3139
3153
|
/**
|
|
3140
3154
|
* Where this player stands on every board, as far as anybody has said.
|
|
3141
3155
|
*
|
|
@@ -3212,7 +3226,7 @@ type Score<Board extends string = string> = {
|
|
|
3212
3226
|
}): Promise<ScoreResult>;
|
|
3213
3227
|
};
|
|
3214
3228
|
//#endregion
|
|
3215
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3229
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/share.d.ts
|
|
3216
3230
|
/**
|
|
3217
3231
|
* @group Share
|
|
3218
3232
|
* @category Important
|
|
@@ -3284,7 +3298,7 @@ type Share = {
|
|
|
3284
3298
|
}): void;
|
|
3285
3299
|
};
|
|
3286
3300
|
//#endregion
|
|
3287
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3301
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/init.d.ts
|
|
3288
3302
|
/**
|
|
3289
3303
|
* What `init` hands back: every namespace a game has, on one object.
|
|
3290
3304
|
*
|
|
@@ -3589,7 +3603,7 @@ type InitOptions = {
|
|
|
3589
3603
|
*/
|
|
3590
3604
|
declare function init<M extends object = GameManifest>(options?: InitOptions): Promise<GameSdk<M>>;
|
|
3591
3605
|
//#endregion
|
|
3592
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3606
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/transport.d.ts
|
|
3593
3607
|
/**
|
|
3594
3608
|
* How long a game with no shim and no parent waits for one to turn up.
|
|
3595
3609
|
*
|
|
@@ -3619,7 +3633,7 @@ type HostTransportOptions = {
|
|
|
3619
3633
|
*/
|
|
3620
3634
|
declare function hostTransport(options?: HostTransportOptions): Promise<Transport>;
|
|
3621
3635
|
//#endregion
|
|
3622
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
3636
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-XAG7tD/game/src/shim.d.ts
|
|
3623
3637
|
/**
|
|
3624
3638
|
* Whether an arbitrary value is a shim this SDK knows how to speak to.
|
|
3625
3639
|
*
|
package/index.js
CHANGED
|
@@ -700,7 +700,7 @@ var SHIM = "__HOPPLE_SHIM__";
|
|
|
700
700
|
var SET_SHIM = "__HOPPLE_SET_SHIM__";
|
|
701
701
|
|
|
702
702
|
// package.json
|
|
703
|
-
var package_default = { version: "0.1.0-rc.
|
|
703
|
+
var package_default = { version: "0.1.0-rc.2" };
|
|
704
704
|
|
|
705
705
|
// src/calls.ts
|
|
706
706
|
function answering(peer) {
|
|
@@ -1381,6 +1381,7 @@ function draw(root, model, report, doc, onError) {
|
|
|
1381
1381
|
root.append(style);
|
|
1382
1382
|
const undo = [];
|
|
1383
1383
|
const floating = model.sticks === "floating";
|
|
1384
|
+
const used = () => root.host.setAttribute("data-used", "");
|
|
1384
1385
|
for (const { zone, controls } of model.zones) {
|
|
1385
1386
|
const element = doc.createElement("div");
|
|
1386
1387
|
element.className = `zone zone-${zone}`;
|
|
@@ -1389,7 +1390,7 @@ function draw(root, model, report, doc, onError) {
|
|
|
1389
1390
|
element.append(buttons(control, report, doc, undo, onError));
|
|
1390
1391
|
continue;
|
|
1391
1392
|
}
|
|
1392
|
-
const drawn = stick(control, floating ? zone : null, report, doc, undo, onError);
|
|
1393
|
+
const drawn = stick(control, floating ? zone : null, report, used, doc, undo, onError);
|
|
1393
1394
|
element.append(drawn.well);
|
|
1394
1395
|
if (drawn.reach !== null) style.after(drawn.reach);
|
|
1395
1396
|
}
|
|
@@ -1436,7 +1437,7 @@ function buttons(control, report, doc, undo, onError) {
|
|
|
1436
1437
|
}
|
|
1437
1438
|
return group2;
|
|
1438
1439
|
}
|
|
1439
|
-
function stick(control, zone, report, doc, undo, onError) {
|
|
1440
|
+
function stick(control, zone, report, used, doc, undo, onError) {
|
|
1440
1441
|
const { axis } = control;
|
|
1441
1442
|
const well = doc.createElement("div");
|
|
1442
1443
|
well.className = axis === void 0 ? "control stick" : `control stick track track-${axis}`;
|
|
@@ -1474,9 +1475,8 @@ function stick(control, zone, report, doc, undo, onError) {
|
|
|
1474
1475
|
function bring(to) {
|
|
1475
1476
|
origin = to;
|
|
1476
1477
|
const from = rest ?? to;
|
|
1477
|
-
well.
|
|
1478
|
-
well.style.setProperty("--
|
|
1479
|
-
well.style.setProperty("--dy", `${to === null || from === null ? 0 : to.y - from.y}px`);
|
|
1478
|
+
well.style.setProperty("--dx", `${to.x - from.x}px`);
|
|
1479
|
+
well.style.setProperty("--dy", `${to.y - from.y}px`);
|
|
1480
1480
|
}
|
|
1481
1481
|
const begin = (event) => {
|
|
1482
1482
|
const pointer = event;
|
|
@@ -1491,7 +1491,11 @@ function stick(control, zone, report, doc, undo, onError) {
|
|
|
1491
1491
|
event.currentTarget?.setPointerCapture?.(pointer.pointerId);
|
|
1492
1492
|
} catch {
|
|
1493
1493
|
}
|
|
1494
|
-
|
|
1494
|
+
well.classList.add("held");
|
|
1495
|
+
if (zone !== null) {
|
|
1496
|
+
bring({ x: pointer.clientX, y: pointer.clientY });
|
|
1497
|
+
used();
|
|
1498
|
+
}
|
|
1495
1499
|
lean(pointer);
|
|
1496
1500
|
};
|
|
1497
1501
|
const lean = (pointer) => {
|
|
@@ -1502,8 +1506,13 @@ function stick(control, zone, report, doc, undo, onError) {
|
|
|
1502
1506
|
const short = axis === "x" ? box.height : box.width;
|
|
1503
1507
|
const travel = long > short ? (long - short) / 2 : long / 2;
|
|
1504
1508
|
if (travel <= 0) return;
|
|
1505
|
-
const
|
|
1509
|
+
const at = axis === "x" ? pointer.clientX : pointer.clientY;
|
|
1510
|
+
const offset = at - (axis === "x" ? centre.x : centre.y);
|
|
1506
1511
|
const along = Math.max(-1, Math.min(1, offset / travel));
|
|
1512
|
+
if (origin !== null && Math.abs(offset) > travel) {
|
|
1513
|
+
const edge = at - along * travel;
|
|
1514
|
+
bring(axis === "x" ? { x: edge, y: origin.y } : { x: origin.x, y: edge });
|
|
1515
|
+
}
|
|
1507
1516
|
send(axis === "x" ? along : 0, axis === "y" ? along : 0);
|
|
1508
1517
|
return;
|
|
1509
1518
|
}
|
|
@@ -1515,6 +1524,9 @@ function stick(control, zone, report, doc, undo, onError) {
|
|
|
1515
1524
|
if (magnitude > 1) {
|
|
1516
1525
|
x /= magnitude;
|
|
1517
1526
|
y /= magnitude;
|
|
1527
|
+
if (origin !== null) {
|
|
1528
|
+
bring({ x: pointer.clientX - x * radius, y: pointer.clientY - y * radius });
|
|
1529
|
+
}
|
|
1518
1530
|
}
|
|
1519
1531
|
send(x, y);
|
|
1520
1532
|
};
|
|
@@ -1530,7 +1542,11 @@ function stick(control, zone, report, doc, undo, onError) {
|
|
|
1530
1542
|
if (holding !== pointer.pointerId) return;
|
|
1531
1543
|
event.stopPropagation();
|
|
1532
1544
|
holding = null;
|
|
1533
|
-
|
|
1545
|
+
well.classList.remove("held");
|
|
1546
|
+
if (zone !== null) {
|
|
1547
|
+
origin = null;
|
|
1548
|
+
well.classList.add("let-go");
|
|
1549
|
+
}
|
|
1534
1550
|
send(0, 0);
|
|
1535
1551
|
};
|
|
1536
1552
|
for (const target of reach === null ? [well] : [well, reach]) {
|
|
@@ -1572,9 +1588,10 @@ var SHEET = `
|
|
|
1572
1588
|
--hopple-pad-radius: 999px;
|
|
1573
1589
|
/* How high a floating stick's side of the screen reaches, from the bottom. */
|
|
1574
1590
|
--hopple-pad-reach: 60%;
|
|
1575
|
-
/* A floating stick at rest is a
|
|
1576
|
-
the stick under a thumb. */
|
|
1577
|
-
--hopple-pad-idle: 0.
|
|
1591
|
+
/* A floating stick at rest is a mark that there is one, drawn quieter than
|
|
1592
|
+
the stick under a thumb, and quieter again once the player has used it. */
|
|
1593
|
+
--hopple-pad-idle: 0.55;
|
|
1594
|
+
--hopple-pad-used: 0.3;
|
|
1578
1595
|
}
|
|
1579
1596
|
.zone {
|
|
1580
1597
|
position: fixed;
|
|
@@ -1627,7 +1644,11 @@ var SHEET = `
|
|
|
1627
1644
|
.knob {
|
|
1628
1645
|
width: 44%; height: 44%; border-radius: 50%;
|
|
1629
1646
|
background: var(--hopple-pad-edge);
|
|
1630
|
-
|
|
1647
|
+
/* As far as the thumb has gone: all the way over is the knob on the rim. */
|
|
1648
|
+
transform: translate(
|
|
1649
|
+
calc(var(--x, 0) * var(--hopple-pad-stick) / 2),
|
|
1650
|
+
calc(var(--y, 0) * var(--hopple-pad-stick) / 2)
|
|
1651
|
+
);
|
|
1631
1652
|
will-change: transform;
|
|
1632
1653
|
}
|
|
1633
1654
|
.track {
|
|
@@ -1673,13 +1694,24 @@ var SHEET = `
|
|
|
1673
1694
|
.reach-left { left: var(--hopple-pad-left); right: 50%; }
|
|
1674
1695
|
.reach-right { left: 50%; right: var(--hopple-pad-right); }
|
|
1675
1696
|
.reach-full { left: var(--hopple-pad-left); right: var(--hopple-pad-right); }
|
|
1676
|
-
.floating { translate: var(--dx, 0px) var(--dy, 0px); }
|
|
1677
|
-
|
|
1697
|
+
.floating.held { translate: var(--dx, 0px) var(--dy, 0px); }
|
|
1698
|
+
/* At rest a mark and not a stick: an outline and a dot, with nothing on it that
|
|
1699
|
+
looks like it has to be pressed there. */
|
|
1700
|
+
.floating:not(.held) { opacity: var(--hopple-pad-idle); background: transparent; }
|
|
1701
|
+
:host([data-used]) .floating:not(.held) { opacity: var(--hopple-pad-used); }
|
|
1702
|
+
.floating:not(.held) .knob { scale: 0.3; }
|
|
1703
|
+
.floating.track:not(.held) .knob { scale: 0; }
|
|
1678
1704
|
@media (prefers-reduced-motion: no-preference) {
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1705
|
+
/* Back to the middle gently; under a thumb at once, since it is already there. */
|
|
1706
|
+
.stick:not(.held) .knob { transition: transform 60ms linear, scale 60ms linear; }
|
|
1707
|
+
/* Let go, it goes out where the thumb left it and the mark comes back where
|
|
1708
|
+
it rests, rather than travelling there as if that were where it lives. */
|
|
1709
|
+
.floating.let-go:not(.held) { animation: let-go 280ms ease-out; }
|
|
1710
|
+
}
|
|
1711
|
+
@keyframes let-go {
|
|
1712
|
+
0% { translate: var(--dx, 0px) var(--dy, 0px); opacity: 1; }
|
|
1713
|
+
40% { translate: var(--dx, 0px) var(--dy, 0px); opacity: 0; }
|
|
1714
|
+
41% { translate: 0px 0px; opacity: 0; }
|
|
1683
1715
|
}
|
|
1684
1716
|
`;
|
|
1685
1717
|
|
|
@@ -1714,7 +1746,6 @@ function createSources(options) {
|
|
|
1714
1746
|
}
|
|
1715
1747
|
function key(event, down) {
|
|
1716
1748
|
const press = event;
|
|
1717
|
-
if (down && press.repeat) return;
|
|
1718
1749
|
if (options.suspended() || typing(scope)) return;
|
|
1719
1750
|
const concrete = `key.${press.code}`;
|
|
1720
1751
|
if (!options.isBound(concrete)) return;
|
|
@@ -3375,6 +3406,12 @@ var GameManifest = {
|
|
|
3375
3406
|
"maxLength": 64,
|
|
3376
3407
|
"errorMessage": "must be lowercase letters, digits and hyphens"
|
|
3377
3408
|
},
|
|
3409
|
+
"owner": {
|
|
3410
|
+
"type": "string",
|
|
3411
|
+
"pattern": "^[a-z0-9]+(?:-[a-z0-9]+)*$",
|
|
3412
|
+
"maxLength": 64,
|
|
3413
|
+
"errorMessage": "must be a handle: lowercase letters, digits and hyphens"
|
|
3414
|
+
},
|
|
3378
3415
|
"version": {
|
|
3379
3416
|
"type": "string",
|
|
3380
3417
|
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$",
|