@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.
@@ -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.1 */
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 u(c,i){a(we(c,i))}let p=c=>{let i=c;if(i.error!==void 0&&i.error!==null){u("uncaught",i.error);return}if(typeof i.message!="string"||i.message==="")return;let m=we("uncaught",i.message);typeof i.filename=="string"&&i.filename!==""&&(m.stack=`at ${i.filename}:${i.lineno}:${i.colno}`),a(m)},d=c=>{u("rejection",c.reason)};return e?.addEventListener("error",p),e?.addEventListener("unhandledrejection",d),{report:u,connect(c){t=c;let i=n;n=[];for(let m of i)c.emit("error.report",m)},dispose(){e?.removeEventListener("error",p),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=ue(e),n=ue(t.app),o=ue(t.game),r=ue(t.player),s=ue(t.device);return Object.freeze({app:Object.freeze({id:le(n.id,B.app.id),version:le(n.version,B.app.version),surface:tn(n.surface,Je,B.app.surface)}),game:Object.freeze({id:le(o.id,B.game.id),version:le(o.version,B.game.version)}),player:Object.freeze({id:typeof r.id=="string"&&r.id!==""?r.id:null,returning:r.returning===!0,locale:le(r.locale,B.player.locale)}),device:Object.freeze({tier:tn(s.tier,Qe,B.device.tier)})})}function ue(e){return typeof e=="object"&&e!==null?e:{}}function le(e,t){return typeof e=="string"?e:t}function tn(e,t,n){return t.includes(e)?e:n}var J=["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"],ht=["pad.left.stick","pad.right.stick"],er=["pad.left.stick","pad.right.stick"];function Pe(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 Me(e,t){return e===t?!0:e==="pad.stick"&&er.includes(t)}function pe(e,t){return e.find(n=>Le(n,t))??e[0]}function Le(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 de(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 fe="__HOPPLE_INPUT__";function me(e){return typeof e!="object"||e===null?!1:e.v===1}var ye=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 u of sr){let p=t[u];if(p===void 0)continue;let d=[];for(let[c,i]of Object.entries(p))d.push(ir(c,i,n));d.length>0&&a.push({zone:u,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}`,u={id:e,type:"thumbstick",shape:o,buttons:[],stick:a};return t.axis!==void 0&&(u.axis=t.axis),u}let r=typeof t.as=="object"&&t.as!==null?t.as:{},s=(t.buttons??[]).map(a=>{let u={action:a,label:ar(n[a])??a},p=r[a];return typeof p=="string"&&(u.as=p),u});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 u(){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(p){this.clear();let d=u();for(let i of rr)o?.style.setProperty(`--hopple-pad-${i}`,`${p.insets[i]}px`);let c=e.shim();if(c!==null&&!a&&typeof c.style=="function")try{c.style(d,{layout:p.layout,frame:p.frame}),a=!0}catch(i){e.onError(i)}if(c!==null&&typeof c.render=="function")try{let i=c.render(d,p,e.report);s=typeof i=="function"?i:null;return}catch(i){e.onError(i),d.replaceChildren(),s=null}s=cr(d,p,e.report,n,e.onError)},clear(){if(s!==null){try{s()}catch(p){e.onError(p)}s=null}r?.replaceChildren()},rects(){let p=[];if(r===null)return p;for(let d of r.querySelectorAll("[data-control]")){let c=d.getBoundingClientRect();c.width<=0||c.height<=0||p.push({x:c.x,y:c.y,width:c.width,height:c.height})}return p},fits(p,d){if(p.width<=0||p.height<=0)return!0;let c=p.width-d.right,i=p.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(p){return o===null?!1:(typeof p.composedPath=="function"?p.composedPath():[]).includes(o)?!0:p.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=[],u=t.sticks==="floating";for(let{zone:p,controls:d}of t.zones){let c=o.createElement("div");c.className=`zone zone-${p}`;for(let i of d){if(i.type==="buttons"){c.append(ur(i,n,o,a,r));continue}let m=lr(i,u?p:null,n,o,a,r);c.append(m.well),m.reach!==null&&s.after(m.reach)}e.append(c)}return()=>{for(let p of a)p()}}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,u]of e.buttons.entries()){let p=n.createElement("button");p.className="btn",p.type="button",p.textContent=u.label,p.setAttribute("aria-label",u.action),p.setAttribute("data-action",u.action);let d=m=>f=>{f.preventDefault(),f.stopPropagation(),p.classList.toggle("down",m);try{t.press(e.id,a,m)}catch(l){r(l)}},c=d(!0),i=d(!1);p.addEventListener("pointerdown",c),p.addEventListener("pointerup",i),p.addEventListener("pointercancel",i),p.addEventListener("pointerleave",i),o.push(()=>{p.removeEventListener("pointerdown",c),p.removeEventListener("pointerup",i),p.removeEventListener("pointercancel",i),p.removeEventListener("pointerleave",i)}),s.append(p)}return s}function lr(e,t,n,o,r,s){let{axis:a}=e,u=o.createElement("div");if(u.className=a===void 0?"control stick":`control stick track track-${a}`,t!==null&&u.classList.add("floating"),u.setAttribute("data-control",e.id),u.setAttribute("aria-label",e.stick),a!==void 0)for(let A of["start","end"]){let x=o.createElement("span");x.className=`chevron chevron-${A}`,u.append(x)}let p=o.createElement("div");p.className="knob",u.append(p);let d=null;t!==null&&(d=o.createElement("div"),d.className=`reach reach-${t}`,d.setAttribute("data-reach",e.id));let c=null,i=null,m=null;function f(A,x){p.style.setProperty("--x",String(A)),p.style.setProperty("--y",String(x));try{n.move(e.id,A,x)}catch(y){s(y)}}function l(A){m=A;let x=i??A;u.classList.toggle("held",A!==null),u.style.setProperty("--dx",`${A===null||x===null?0:A.x-x.x}px`),u.style.setProperty("--dy",`${A===null||x===null?0:A.y-x.y}px`)}let b=A=>{let x=A;if(A.preventDefault(),A.stopPropagation(),t!==null&&i===null){let y=u.getBoundingClientRect();i={x:y.x+y.width/2,y:y.y+y.height/2}}c=x.pointerId;try{A.currentTarget?.setPointerCapture?.(x.pointerId)}catch{}t!==null&&l({x:x.clientX,y:x.clientY}),v(x)},v=A=>{let x=u.getBoundingClientRect(),y=m??{x:x.x+x.width/2,y:x.y+x.height/2};if(a!==void 0){let w=a==="x"?x.width:x.height,O=a==="x"?x.height:x.width,T=w>O?(w-O)/2:w/2;if(T<=0)return;let $=a==="x"?A.clientX-y.x:A.clientY-y.y,C=Math.max(-1,Math.min(1,$/T));f(a==="x"?C:0,a==="y"?C:0);return}let E=Math.min(x.width,x.height)/2;if(E<=0)return;let k=(A.clientX-y.x)/E,P=(A.clientY-y.y)/E,g=Math.hypot(k,P);g>1&&(k/=g,P/=g),f(k,P)},M=A=>{let x=A;c===x.pointerId&&(A.preventDefault(),A.stopPropagation(),v(x))},I=A=>{c===A.pointerId&&(A.stopPropagation(),c=null,t!==null&&l(null),f(0,0))};for(let A of d===null?[u]:[u,d])A.addEventListener("pointerdown",b),A.addEventListener("pointermove",M),A.addEventListener("pointerup",I),A.addEventListener("pointercancel",I),r.push(()=>{A.removeEventListener("pointerdown",b),A.removeEventListener("pointermove",M),A.removeEventListener("pointerup",I),A.removeEventListener("pointercancel",I)});return{well:u,reach:d}}var pr=`
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 sign that there is one, drawn quieter than
30
- the stick under a thumb. */
31
- --hopple-pad-idle: 0.6;
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
- transform: translate(calc(var(--x, 0) * 40%), calc(var(--y, 0) * 40%));
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
- .floating:not(.held) { opacity: var(--hopple-pad-idle); }
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
- .knob { transition: transform 60ms linear; }
134
- /* Back to rest gently; to a thumb at once, since it is already there. */
135
- .floating { transition: translate 140ms ease-out, opacity 140ms ease-out; }
136
- .floating.held { transition: none; }
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-1wJl1B/protocol/src/achievement.d.ts
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-1wJl1B/protocol/src/analytics.d.ts
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-1wJl1B/protocol/src/audio.d.ts
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-1wJl1B/protocol/src/errors.d.ts
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-1wJl1B/protocol/src/envelope.d.ts
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-1wJl1B/protocol/src/input.d.ts
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 drawn where the layout puts it, as a sign that there is a
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. `fixed` stays where it is drawn and is moved only from there.
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-1wJl1B/protocol/src/panel.d.ts
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-1wJl1B/protocol/src/pause.d.ts
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-1wJl1B/protocol/src/save.d.ts
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-1wJl1B/protocol/src/score.d.ts
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-1wJl1B/protocol/src/share.d.ts
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-1wJl1B/protocol/src/methods.d.ts
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-1wJl1B/protocol/src/listeners.d.ts
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-1wJl1B/protocol/src/transport.d.ts
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-1wJl1B/manifest/src/manifest.d.ts
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-1wJl1B/game/src/achievements.d.ts
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-1wJl1B/game/src/analytics.d.ts
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-1wJl1B/game/src/audio.d.ts
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-1wJl1B/game/src/info.d.ts
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-1wJl1B/game/src/layout.d.ts
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-1wJl1B/game/src/names.d.ts
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-1wJl1B/game/src/input/state.d.ts
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-1wJl1B/game/src/input/controls.d.ts
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`: drawn where the layout puts it, so a player can see there is a
2487
- * stick, and brought to wherever a thumb lands on its side of the screen, so
2488
- * the place they first touch is the centre. `fixed`: moved only from where
2489
- * it is drawn. A renderer that ignores this draws `fixed`, which is still a
2490
- * stick a game can be played with.
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-1wJl1B/game/src/input/bindings.d.ts
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-1wJl1B/game/src/input/index.d.ts
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-1wJl1B/game/src/lifecycle.d.ts
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-1wJl1B/game/src/loop.d.ts
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-1wJl1B/game/src/panel.d.ts
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-1wJl1B/game/src/pause.d.ts
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-1wJl1B/game/src/resources.d.ts
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-1wJl1B/game/src/save.d.ts
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-1wJl1B/game/src/score.d.ts
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-1wJl1B/game/src/share.d.ts
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-1wJl1B/game/src/init.d.ts
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-1wJl1B/game/src/transport.d.ts
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-1wJl1B/game/src/shim.d.ts
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.1" };
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.classList.toggle("held", to !== null);
1478
- well.style.setProperty("--dx", `${to === null || from === null ? 0 : to.x - from.x}px`);
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
- if (zone !== null) bring({ x: pointer.clientX, y: pointer.clientY });
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 offset = axis === "x" ? pointer.clientX - centre.x : pointer.clientY - centre.y;
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
- if (zone !== null) bring(null);
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 sign that there is one, drawn quieter than
1576
- the stick under a thumb. */
1577
- --hopple-pad-idle: 0.6;
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
- transform: translate(calc(var(--x, 0) * 40%), calc(var(--y, 0) * 40%));
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
- .floating:not(.held) { opacity: var(--hopple-pad-idle); }
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
- .knob { transition: transform 60ms linear; }
1680
- /* Back to rest gently; to a thumb at once, since it is already there. */
1681
- .floating { transition: translate 140ms ease-out, opacity 140ms ease-out; }
1682
- .floating.held { transition: none; }
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.-]+)?$",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hopple/sdk",
3
- "version": "0.1.0-rc.1",
3
+ "version": "0.1.0-rc.2",
4
4
  "description": "The Hopple SDK: what a game imports to talk to the host that runs it.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://hopple.app",