@mblinkov/whats-missing 20.0.21 → 20.0.22

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.
@@ -1,10 +1,10 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),r=require("react"),ce=window.origin+"/cloud/speakid/games/whatsmissing/",D=(a,m)=>a.map(([c,s])=>({src:`${ce}${m}/${s.replace(/\s/g,"%20")}.png`,name:s})),me=[["apple","apple"],["banana","banana"],["bread","bread"],["burger","burger"],["cake","cake"],["carrot","carrot"],["cheese","cheese"],["chicken","chicken"],["chocolate","chocolate"],["corn","corn"],["donut","donut"],["fish","fish"],["fries","fries"],["grapes","grapes"],["ice_cream","ice cream"],["milk","milk"],["orange","orange"],["pear","pear"],["pizza","pizza"],["rice","rice"],["sandwich","sandwich"],["soup","soup"],["strawberry","strawberry"],["tomato","tomato"],["watermelon","watermelon"]],ue=[["bear","bear"],["cat","cat"],["crocodile","crocodile"],["dolphin","dolphin"],["elephant","elephant"],["fox","fox"],["giraffe","giraffe"],["horse","horse"],["kangaroo","kangaroo"],["lion","lion"],["monkey","monkey"],["owl","owl"],["panda","panda"],["penguin","penguin"],["squirrel","squirrel"],["tiger","tiger"],["tortoise","tortoise"],["wolf","wolf"],["zebra","zebra"]],ge=[["ball","ball"],["balloon","balloon"],["blocks","blocks"],["car","car"],["doll","doll"],["drum","drum"],["guitar","guitar"],["jump_rope","jump rope"],["kite","kite"],["paints","paints"],["plane","plane"],["puzzle","puzzle"],["rocket","rocket"],["scooter","scooter"],["skateboard","skateboard"],["slide","slide"],["teddy_bear","teddy bear"],["yo-yo","yo-yo"],["crayons","crayons"]],P={food:D(me,"food"),animals:D(ue,"animals"),toys:D(ge,"toys")},he=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),s=require("react"),pe=window.origin+"/cloud/speakid/games/whatsmissing/",q=(d,f)=>d.map(([g,i])=>({src:`${pe}${f}/${i.replace(/\s/g,"%20")}.png`,name:i})),ye=[["apple","apple"],["banana","banana"],["bread","bread"],["burger","burger"],["cake","cake"],["carrot","carrot"],["cheese","cheese"],["chicken","chicken"],["chocolate","chocolate"],["corn","corn"],["donut","donut"],["fish","fish"],["fries","fries"],["grapes","grapes"],["ice_cream","ice cream"],["milk","milk"],["orange","orange"],["pear","pear"],["pizza","pizza"],["rice","rice"],["sandwich","sandwich"],["soup","soup"],["strawberry","strawberry"],["tomato","tomato"],["watermelon","watermelon"]],xe=[["bear","bear"],["cat","cat"],["crocodile","crocodile"],["dolphin","dolphin"],["elephant","elephant"],["fox","fox"],["giraffe","giraffe"],["horse","horse"],["kangaroo","kangaroo"],["lion","lion"],["monkey","monkey"],["owl","owl"],["panda","panda"],["penguin","penguin"],["squirrel","squirrel"],["tiger","tiger"],["tortoise","tortoise"],["wolf","wolf"],["zebra","zebra"]],be=[["ball","ball"],["balloon","balloon"],["blocks","blocks"],["car","car"],["doll","doll"],["drum","drum"],["guitar","guitar"],["jump_rope","jump rope"],["kite","kite"],["paints","paints"],["plane","plane"],["puzzle","puzzle"],["rocket","rocket"],["scooter","scooter"],["skateboard","skateboard"],["slide","slide"],["teddy_bear","teddy bear"],["yo-yo","yo-yo"],["crayons","crayons"]],V={food:q(ye,"food"),animals:q(xe,"animals"),toys:q(be,"toys")},we=`
2
2
  @keyframes spin {
3
3
  from { transform: rotate(0deg); }
4
4
  to { transform: rotate(360deg); }
5
5
  }
6
- `;if(typeof document<"u"&&!document.getElementById("spin-keyframes")){const a=document.createElement("style");a.id="spin-keyframes",a.innerHTML=he,document.head.appendChild(a)}const fe={animation:"spin 1.4s linear infinite"},n={gmCenterScreen:{position:"relative",zIndex:1,minHeight:"100%",width:"100%",display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",textAlign:"center",color:"#1f2937",padding:"24px 16px",boxSizing:"border-box",transform:"translateY(20px)"},gmHeadline1:{fontWeight:700,fontSize:"clamp(28px, 4vw, 40px)",lineHeight:"110%"},gmHeadline3:{fontWeight:600,fontSize:"18px",lineHeight:"130%"},gmBodyM:{fontWeight:400,fontSize:"16px",lineHeight:"140%"},gmBodyS:{fontWeight:400,fontSize:"14px",lineHeight:"140%",color:"#6b7280"},gmButton:{fontFamily:'"Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Noto Sans"',fontWeight:600,fontSize:"16px",padding:"10px 16px",borderRadius:"12px",border:"1px solid #e5e7eb",background:"#ec4c44",color:"#ffffff",cursor:"pointer",boxShadow:"0 6px 18px rgba(236, 76, 68, .18)",transition:"transform .06s ease, box-shadow .2s ease, background .2s ease, opacity .2s ease"},gmButtonActive:{background:"#333",color:"#fff"},gmGameLayout:{position:"relative",width:"100%",maxWidth:"600px",minHeight:"100%",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",textAlign:"center",color:"#1f2937",padding:"16px 8px",margin:"0 auto"},gmGrid:{width:"100%",maxWidth:"calc(210px * 3 + 20px * 2)",margin:"0 auto",display:"grid",gridTemplateColumns:"repeat(3, 210px)",gridAutoRows:"210px",gap:"20px",justifyContent:"center"},gmCard:{border:"1px solid #e5e7eb",borderRadius:"16px",background:"#f9f9f9",aspectRatio:"1 / 1",display:"flex",alignItems:"center",justifyContent:"center",overflow:"hidden",transition:"transform .2s ease, box-shadow .2s ease, border-color .2s ease"},gmCorrect:{border:"2px solid #10b981",boxShadow:"0 0 18px rgba(16,185,129,.45)",background:"#ecfdf5"},gmWrong:{border:"2px solid #ec4c44",boxShadow:"0 0 18px rgba(236,76,68,.35)",background:"#fef2f2"},gmInput:{padding:"6px 10px",borderRadius:"6px",border:"1px solid #ccc",fontSize:"16px",fontFamily:'"Geist", system-ui',width:"160px"},gmTable:{marginTop:"20px",marginBottom:"32px",borderCollapse:"collapse",width:"100%",maxWidth:"520px",tableLayout:"fixed",textAlign:"center"},gmTableCell:{padding:"8px 12px",borderBottom:"1px solid #e5e7eb",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},gmLogoFixed:{position:"absolute",top:"16px",left:"24px",width:"120px",zIndex:10,pointerEvents:"none",background:"transparent",transform:"none",willChange:"auto"},gmLogoImg:{height:"clamp(28px, 5vw, 40px)",width:"auto",background:"transparent",objectFit:"contain",imageRendering:"auto",transform:"translateZ(0)",backfaceVisibility:"hidden",WebkitFontSmoothing:"antialiased"},gmHourglass:{fontSize:"42px",...fe}},pe=()=>{const a=document.createElement("style");a.textContent=`
6
+ `;if(typeof document<"u"&&!document.getElementById("spin-keyframes")){const d=document.createElement("style");d.id="spin-keyframes",d.innerHTML=we,document.head.appendChild(d)}const ve={animation:"spin 1.4s linear infinite"},n={gmCenterScreen:{position:"relative",zIndex:1,minHeight:"100%",width:"100%",display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",textAlign:"center",color:"#1f2937",padding:"24px 16px",boxSizing:"border-box",transform:"translateY(20px)"},gmHeadline1:{fontWeight:700,fontSize:"clamp(28px, 4vw, 40px)",lineHeight:"110%"},gmHeadline3:{fontWeight:600,fontSize:"18px",lineHeight:"130%"},gmBodyM:{fontWeight:400,fontSize:"16px",lineHeight:"140%"},gmBodyS:{fontWeight:400,fontSize:"14px",lineHeight:"140%",color:"#6b7280"},gmButton:{fontFamily:'"Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, "Noto Sans"',fontWeight:600,fontSize:"16px",padding:"10px 16px",borderRadius:"12px",border:"1px solid #e5e7eb",background:"#ec4c44",color:"#ffffff",cursor:"pointer",boxShadow:"0 6px 18px rgba(236, 76, 68, .18)",transition:"transform .06s ease, box-shadow .2s ease, background .2s ease, opacity .2s ease"},gmButtonActive:{background:"#333",color:"#fff"},gmGameLayout:{position:"relative",width:"100%",maxWidth:"600px",minHeight:"100%",display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",textAlign:"center",color:"#1f2937",padding:"16px 8px",margin:"0 auto"},gmGrid:{width:"100%",maxWidth:"calc(210px * 3 + 20px * 2)",margin:"0 auto",display:"grid",gridTemplateColumns:"repeat(3, 210px)",gridAutoRows:"210px",gap:"20px",justifyContent:"center"},gmCard:{border:"1px solid #e5e7eb",borderRadius:"16px",background:"#f9f9f9",aspectRatio:"1 / 1",display:"flex",alignItems:"center",justifyContent:"center",overflow:"hidden",transition:"transform .2s ease, box-shadow .2s ease, border-color .2s ease"},gmCorrect:{border:"2px solid #10b981",boxShadow:"0 0 18px rgba(16,185,129,.45)",background:"#ecfdf5"},gmWrong:{border:"2px solid #ec4c44",boxShadow:"0 0 18px rgba(236,76,68,.35)",background:"#fef2f2"},gmInput:{padding:"6px 10px",borderRadius:"6px",border:"1px solid #ccc",fontSize:"16px",fontFamily:'"Geist", system-ui',width:"160px"},gmTable:{marginTop:"20px",marginBottom:"32px",borderCollapse:"collapse",width:"100%",maxWidth:"520px",tableLayout:"fixed",textAlign:"center"},gmTableCell:{padding:"8px 12px",borderBottom:"1px solid #e5e7eb",whiteSpace:"nowrap",overflow:"hidden",textOverflow:"ellipsis"},gmLogoFixed:{position:"absolute",top:"16px",left:"24px",width:"120px",zIndex:10,pointerEvents:"none",background:"transparent",transform:"none",willChange:"auto"},gmLogoImg:{height:"clamp(28px, 5vw, 40px)",width:"auto",background:"transparent",objectFit:"contain",imageRendering:"auto",transform:"translateZ(0)",backfaceVisibility:"hidden",WebkitFontSmoothing:"antialiased"},gmHourglass:{fontSize:"42px",...ve}},je=()=>{const d=document.createElement("style");d.textContent=`
7
7
  /* scope only inside game root */
8
8
  #whats-missing-root, #whats-missing-root * { box-sizing: border-box; }
9
9
  #whats-missing-root img { max-width:100%; height:auto; display:block; user-select:none; }
10
- `,document.head.appendChild(a)},ye=(a,m)=>{const c=Array.from({length:a.length+1},()=>Array(m.length+1).fill(0));for(let s=0;s<=a.length;s++)c[s][0]=s;for(let s=0;s<=m.length;s++)c[0][s]=s;for(let s=1;s<=a.length;s++)for(let u=1;u<=m.length;u++)c[s][u]=a[s-1]===m[u-1]?c[s-1][u-1]:Math.min(c[s-1][u-1],c[s][u-1],c[s-1][u])+1;return c[a.length][m.length]};function xe(a,m={minDesktopWidth:1200,forceResize:!1}){const c=r.useRef(null),s=m.minDesktopWidth??1200,u=!!m.forceResize;r.useLayoutEffect(()=>{if(typeof window>"u"||!a||!a.current)return;const l=a.current,A=()=>{try{const i=document.body,d=getComputedStyle(i),p=parseFloat(i.style&&i.style.zoom||"");if(p&&!isNaN(p)&&p>0)return p;const v=parseFloat(d.zoom||"");if(v&&!isNaN(v)&&v>0)return v;const k=d.transform||d.webkitTransform||d.mozTransform;if(k&&k!=="none"){const z=k.match(/matrix\(([^)]+)\)/);if(z){const j=z[1].split(",").map(E=>parseFloat(E))[0];if(j&&!isNaN(j))return j}}}catch{}return 1},b=()=>{if(s&&window.innerWidth<s){S();return}const i=A();if(!i||i===1){S();return}c.current||(c.current={transform:l.style.transform||null,transformOrigin:l.style.transformOrigin||null,width:l.style.width||null,height:l.style.height||null,willChange:l.style.willChange||null});const d=1/i;u&&(l.style.width=`${i*100}%`,l.style.height=`${i*100}%`),l.style.transform=`scale(${d})`,l.style.transformOrigin="top left",l.style.willChange="transform",typeof process<"u"&&process.env&&process.env.NODE_ENV==="development"&&console.debug("[useNeutralizeBodyZoom] applied scale:",{scale:i,inv:d,forceResize:u})},S=()=>{if(!c.current)return;const i=c.current;i.transform===null?l.style.removeProperty("transform"):l.style.transform=i.transform,i.transformOrigin===null?l.style.removeProperty("transform-origin"):l.style.transformOrigin=i.transformOrigin,i.width===null?l.style.removeProperty("width"):l.style.width=i.width,i.height===null?l.style.removeProperty("height"):l.style.height=i.height,i.willChange===null?l.style.removeProperty("will-change"):l.style.willChange=i.willChange,c.current=null,process.env.NODE_ENV==="development"&&console.debug("[useNeutralizeBodyZoom] restored original styles")};let x=0;const w=()=>{cancelAnimationFrame(x),x=requestAnimationFrame(b)},f=new MutationObserver(i=>{for(const d of i)if(d.type==="attributes"&&(d.attributeName==="style"||d.attributeName==="class")){w();break}});return f.observe(document.body,{attributes:!0,attributeFilter:["style","class"]}),window.addEventListener("resize",w),w(),()=>{cancelAnimationFrame(x),f.disconnect(),window.removeEventListener("resize",w),S()}},[a,s,u])}function be(){const a=r.useRef(null);xe(a,{minDesktopWidth:1200,forceResize:!1}),r.useEffect(()=>(pe(),()=>{document.body.style.overflow=""}),[]);const[m,c]=r.useState(null),[s,u]=r.useState([]),[l,A]=r.useState(4),[b,S]=r.useState(!1),[x,w]=r.useState(1),[f,i]=r.useState(null),[d,p]=r.useState("ready"),[v,k]=r.useState(3),[z,M]=r.useState(10),[j,E]=r.useState(20),[Q,H]=r.useState(0),[L,F]=r.useState(!1),[B,q]=r.useState(""),[C,W]=r.useState(!1),[N,V]=r.useState(!1),[Y,I]=r.useState(null),[ee,G]=r.useState([]),[te,$]=r.useState([]),[y,ne]=r.useState(!1),[we,Z]=r.useState(1),[_,U]=r.useState(null);r.useEffect(()=>{const t=()=>{const g=window.visualViewport,h=g?Math.round(g.width):window.innerWidth,T=g?Math.round(g.height):window.innerHeight,R=h<768;if(ne(R),R)U(null),Z(1);else{const le=Math.min(h,T),ae=Math.max(360,Math.round(le*.97)),de=Math.min(ae,1e3);U(de),Z(1)}};t(),window.addEventListener("resize",t);const o=window.visualViewport;return o&&typeof o.addEventListener=="function"&&(o.addEventListener("resize",t),o.addEventListener("scroll",t)),()=>{window.removeEventListener("resize",t),o&&typeof o.removeEventListener=="function"&&(o.removeEventListener("resize",t),o.removeEventListener("scroll",t))}},[]);const K=t=>[...t].sort(()=>Math.random()-.5).slice(0,6),X=()=>{if(!m)return;const t=K(P[m]);u(t),S(!0),F(!1),w(1),H(0),G([]),$([]),J(t,[],!0)},J=(t=s,o=te,g=!1)=>{const h=K(t.length?t:P[m]);let T=Math.floor(Math.random()*h.length),R=h[T].name,O=0;for(;o.includes(R)&&O<20;)T=Math.floor(Math.random()*h.length),R=h[T].name,O++;u(h),i(T),$([...o,R]),W(!1),q(""),E(20),I(null),g?(p("ready"),k(3),M(10)):(p("memorize"),M(10))};r.useEffect(()=>{if(!(!b||L||C)){if(d==="ready")if(v<=0)p("memorize");else{const t=setTimeout(()=>k(o=>o-1),1e3);return()=>clearTimeout(t)}if(d==="memorize")if(z<=0)p("guess");else{const t=setTimeout(()=>M(o=>o-1),1e3);return()=>clearTimeout(t)}if(d==="guess"){if(j<=0){W(!0),I("wrong");const o=s[f].name;G(g=>[...g,{round:x,answer:B,correct:o,result:"wrong"}]);return}const t=setTimeout(()=>E(o=>o-1),1e3);return()=>clearTimeout(t)}}},[d,v,z,j,b,L,C,s,f,x,B]);const se=()=>{if(f===null||N)return;const t=s[f].name,o=B.toLowerCase().trim();let g="wrong";o===t?(H(h=>h+1),I("correct"),g="correct"):ye(o,t)===1?(H(h=>h+.5),I("almost"),g="almost"):(I("wrong"),g="wrong"),G(h=>[...h,{round:x,answer:o,correct:t,result:g}]),V(!0),setTimeout(()=>{V(!1),W(!0)},600)},oe=()=>x<l?(w(t=>t+1),J()):F(!0),re=()=>{S(!1),F(!1),c(null)},ie=r.useMemo(()=>e.jsx("div",{style:{...n.gmLogoFixed,position:"absolute",top:16,left:16,zIndex:30},children:e.jsxs("picture",{children:[e.jsx("source",{srcSet:window.origin+"/cloud/speakid/games/whatsmissing/logo.svg",type:"image/svg+xml"}),e.jsx("img",{src:window.origin+"/cloud/speakid/games/whatsmissing/logo.png",alt:"SPEAKID Logo",style:n.gmLogoImg,loading:"lazy"})]})}),[]);return e.jsx("div",{ref:a,style:{width:"100%",height:"100vh",display:"flex",justifyContent:"center",alignItems:"center",background:"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",transition:"background 0.3s ease",overflowX:"clip"},children:e.jsx("div",{style:{width:y?"100%":_||1e3,height:y?"100%":_||1e3,display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",borderRadius:y?0:"20px",background:"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",boxShadow:y?"none":"0 0 40px rgba(0,0,0,0.1)",margin:y?"0 auto":"unset",position:"relative"},children:e.jsx("div",{style:{transform:"none",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center"},children:e.jsxs("div",{id:"whats-missing-root",children:[!y&&ie,!m&&!b&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsx("h1",{style:n.gmHeadline1,children:"WHAT'S MISSING?"}),e.jsx("p",{style:n.gmBodyM,children:"Select a theme:"}),e.jsx("div",{style:{display:"flex",gap:16},children:["animals","food","toys"].map(t=>e.jsx("button",{style:n.gmButton,onClick:()=>c(t),children:t==="animals"?"🐶 Animals":t==="food"?"🍎 Food":"🧸 Toys"},t))}),e.jsxs("div",{style:{marginTop:24},children:[e.jsx("p",{style:n.gmBodyS,children:"Choose number of rounds:"}),e.jsx("div",{style:{display:"flex",gap:12,marginTop:8},children:[3,4,5].map(t=>e.jsx("button",{style:{...n.gmButton,...l===t?n.gmButtonActive:{}},onClick:()=>A(t),children:t},t))})]})]}),m&&!b&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsxs("h1",{style:n.gmHeadline1,children:["Theme selected: ",m]}),e.jsxs("p",{style:n.gmBodyM,children:["Rounds: ",l]}),e.jsx("button",{style:n.gmButton,onClick:X,children:"▶ Start game"})]}),L&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsx("h1",{style:n.gmHeadline1,children:"Results"}),e.jsxs("h2",{style:n.gmHeadline3,children:["Your score: ",Q," / ",l]}),e.jsx("p",{style:{...n.gmBodyM,color:"#10b981",marginTop:12},children:"Yahoo! You did it! 🍬✨"}),e.jsxs("table",{style:n.gmTable,children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Round"}),e.jsx("th",{children:"Your Answer"}),e.jsx("th",{children:"Correct"}),e.jsx("th",{children:"Result"})]})}),e.jsx("tbody",{children:ee.map((t,o)=>e.jsxs("tr",{children:[e.jsx("td",{style:n.gmTableCell,children:t.round}),e.jsx("td",{style:n.gmTableCell,children:t.answer||"—"}),e.jsx("td",{style:n.gmTableCell,children:t.correct}),e.jsx("td",{style:n.gmTableCell,children:t.result==="correct"?"✔ Correct":t.result==="almost"?"◐ Almost (0.5)":"✘ Wrong"})]},o))})]}),e.jsxs("div",{style:{display:"flex",gap:12,marginTop:24},children:[e.jsx("button",{style:n.gmButton,onClick:X,children:"🔁 Play again"}),e.jsx("button",{style:n.gmButton,onClick:re,children:"⬅️ Choose theme"})]})]}),b&&!L&&e.jsxs("div",{style:n.gmGameLayout,children:[e.jsxs("div",{style:{minHeight:160,display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:[d==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("h1",{style:{...n.gmHeadline1,color:"#ec4c44"},children:"GET READY"}),e.jsx("div",{style:n.gmHourglass,children:"⏳"})]}),d==="memorize"&&e.jsxs("p",{style:{...n.gmBodyM,color:"#10b981"},children:["MEMORIZE (",z,")"]}),d==="guess"&&!C&&e.jsxs("p",{style:n.gmBodyM,children:["⏳ Time left: ",j,"s"]})]}),d!=="ready"&&e.jsx("div",{style:{...n.gmGrid,gridTemplateColumns:y?"repeat(2, 1fr)":"repeat(3, 210px)",gridAutoRows:y?"150px":"210px",gap:y?"12px":"20px",justifyItems:"center"},children:s.map((t,o)=>{const g=f===o&&d==="guess"&&!C;return e.jsx("div",{style:{...n.gmCard,...Y==="correct"&&f===o?n.gmCorrect:{},...Y==="wrong"&&f===o?n.gmWrong:{}},children:!g&&e.jsx("img",{src:t.src,alt:t.name,style:{width:"100%",height:"100%",objectFit:"cover"}})},o)})}),e.jsxs("div",{style:{marginTop:16,height:80},children:[d==="guess"&&!C&&e.jsxs("div",{children:[e.jsx("input",{type:"text",placeholder:"Type the missing word",value:B,onChange:t=>q(t.target.value),style:n.gmInput}),e.jsx("button",{style:{...n.gmButton,marginLeft:8},onClick:se,disabled:N,children:N?"...":"Check"})]}),C&&e.jsx("button",{type:"button",style:n.gmButton,onClick:oe,children:"Next round"})]})]})]})})})})}exports.Game=be;exports.themes=P;
10
+ `,document.head.appendChild(d)},Se=(d,f)=>{const g=Array.from({length:d.length+1},()=>Array(f.length+1).fill(0));for(let i=0;i<=d.length;i++)g[i][0]=i;for(let i=0;i<=f.length;i++)g[0][i]=i;for(let i=1;i<=d.length;i++)for(let m=1;m<=f.length;m++)g[i][m]=d[i-1]===f[m-1]?g[i-1][m-1]:Math.min(g[i-1][m-1],g[i][m-1],g[i-1][m])+1;return g[d.length][f.length]};function ze(d,f={minDesktopWidth:1200,forceResize:!1}){const g=s.useRef(null),i=f.minDesktopWidth??1200,m=!!f.forceResize;s.useLayoutEffect(()=>{if(typeof window>"u"||!d||!d.current)return;const l=d.current,z=()=>{try{const r=document.body,y=getComputedStyle(r),p=parseFloat(r.style&&r.style.zoom||"");if(p&&!isNaN(p)&&p>0)return p;const C=parseFloat(y.zoom||"");if(C&&!isNaN(C)&&C>0)return C;const c=document.createElement("div");c.style.position="absolute",c.style.left="0",c.style.top="0",c.style.width="100px",c.style.height="1px",c.style.visibility="hidden",c.style.pointerEvents="none",document.documentElement.appendChild(c);const S=c.getBoundingClientRect();if(document.documentElement.removeChild(c),S&&S.width>0){const k=S.width/100;if(!isNaN(k)&&k>0)return k}}catch{}return 1},L=()=>{if(i&&window.innerWidth<i){j();return}const r=z();if(!r||r===1){j();return}g.current||(g.current={transform:l.style.transform||null,transformOrigin:l.style.transformOrigin||null,width:l.style.width||null,height:l.style.height||null,willChange:l.style.willChange||null});const y=1/r;m&&(l.style.width=`${r*100}%`,l.style.height=`${r*100}%`),l.style.transformOrigin="50% 50%",l.style.transform=`scale(${y})`,l.style.willChange="transform",typeof process<"u"&&process.env?.NODE_ENV==="development"&&console.debug("[useNeutralizeBodyZoom] applied scale:",{scale:r,inv:y,forceResize:m})},j=()=>{if(!g.current)return;const r=g.current;r.transform===null?l.style.removeProperty("transform"):l.style.transform=r.transform,r.transformOrigin===null?l.style.removeProperty("transform-origin"):l.style.transformOrigin=r.transformOrigin,r.width===null?l.style.removeProperty("width"):l.style.width=r.width,r.height===null?l.style.removeProperty("height"):l.style.height=r.height,r.willChange===null?l.style.removeProperty("will-change"):l.style.willChange=r.willChange,g.current=null,typeof process<"u"&&process.env?.NODE_ENV==="development"&&console.debug("[useNeutralizeBodyZoom] restored original styles")};let M=0;const x=()=>{cancelAnimationFrame(M),M=requestAnimationFrame(L)},T=new MutationObserver(r=>{for(const y of r)if(y.type==="attributes"&&(y.attributeName==="style"||y.attributeName==="class")){x();break}});return T.observe(document.body,{attributes:!0,attributeFilter:["style","class"]}),window.addEventListener("resize",x),x(),()=>{cancelAnimationFrame(M),T.disconnect(),window.removeEventListener("resize",x),j()}},[d,i,m])}function Ce(){const d=s.useRef(null),f=s.useRef(null),[g,i]=s.useState(null);ze(d,{minDesktopWidth:1200,forceResize:!1}),s.useEffect(()=>(je(),()=>{document.body.style.overflow=""}),[]);const[m,l]=s.useState(null),[z,L]=s.useState([]),[j,M]=s.useState(4),[x,T]=s.useState(!1),[r,y]=s.useState(1),[p,C]=s.useState(null),[c,S]=s.useState("ready"),[k,Y]=s.useState(3),[A,W]=s.useState(10),[F,Z]=s.useState(20),[oe,N]=s.useState(0),[H,O]=s.useState(!1),[B,_]=s.useState(""),[R,$]=s.useState(!1),[G,U]=s.useState(!1),[K,E]=s.useState(null),[se,D]=s.useState([]),[ie,J]=s.useState([]),[b,re]=s.useState(!1),[I,Q]=s.useState(null);s.useLayoutEffect(()=>{const t=f.current;if(!t||typeof window>"u")return;let o=null;try{o=new ResizeObserver(u=>{for(const a of u){const h=a.contentRect;i(Math.round(Math.min(h.width,h.height)))}}),o.observe(t),i(Math.round(Math.min(t.clientWidth,t.clientHeight)))}catch{i(Math.round(Math.min(t.clientWidth,t.clientHeight)))}return()=>{o&&o.disconnect()}},[b]),s.useEffect(()=>{const t=()=>{const u=window.visualViewport,a=u?Math.round(u.width):window.innerWidth,h=u?Math.round(u.height):window.innerHeight,w=a<768||a>=768&&a<1200&&h>a;if(re(w),w){Q(null);return}const v=.92,P=1e3,ne=40,me=Math.max(360,a-ne),ue=Math.max(360,h-ne),ge=Math.min(me,ue),he=Math.max(360,Math.round(ge*v)),fe=Math.min(he,P);Q(fe)};t(),window.addEventListener("resize",t);const o=window.visualViewport;return o&&typeof o.addEventListener=="function"&&(o.addEventListener("resize",t),o.addEventListener("scroll",t)),()=>{window.removeEventListener("resize",t),o&&typeof o.removeEventListener=="function"&&(o.removeEventListener("resize",t),o.removeEventListener("scroll",t))}},[]);const X=t=>[...t].sort(()=>Math.random()-.5).slice(0,6),ee=()=>{if(!m)return;const t=X(V[m]);L(t),T(!0),O(!1),y(1),N(0),D([]),J([]),te(t,[],!0)},te=(t=z,o=ie,u=!1)=>{const a=X(t.length?t:V[m]);let h=Math.floor(Math.random()*a.length),w=a[h].name,v=0;for(;o.includes(w)&&v<20;)h=Math.floor(Math.random()*a.length),w=a[h].name,v++;L(a),C(h),J([...o,w]),$(!1),_(""),Z(20),E(null),u?(S("ready"),Y(3),W(10)):(S("memorize"),W(10))};s.useEffect(()=>{if(!(!x||H||R)){if(c==="ready")if(k<=0)S("memorize");else{const t=setTimeout(()=>Y(o=>o-1),1e3);return()=>clearTimeout(t)}if(c==="memorize")if(A<=0)S("guess");else{const t=setTimeout(()=>W(o=>o-1),1e3);return()=>clearTimeout(t)}if(c==="guess"){if(F<=0){$(!0),E("wrong");const o=z[p].name;D(u=>[...u,{round:r,answer:B,correct:o,result:"wrong"}]);return}const t=setTimeout(()=>Z(o=>o-1),1e3);return()=>clearTimeout(t)}}},[c,k,A,F,x,H,R,z,p,r,B]);const le=()=>{if(p===null||G)return;const t=z[p].name,o=B.toLowerCase().trim();let u="wrong";o===t?(N(a=>a+1),E("correct"),u="correct"):Se(o,t)===1?(N(a=>a+.5),E("almost"),u="almost"):(E("wrong"),u="wrong"),D(a=>[...a,{round:r,answer:o,correct:t,result:u}]),U(!0),setTimeout(()=>{U(!1),$(!0)},600)},ae=()=>r<j?(y(t=>t+1),te()):O(!0),de=()=>{T(!1),O(!1),l(null)},ce=s.useMemo(()=>e.jsx("div",{style:{...n.gmLogoFixed,position:"absolute",top:16,left:16,zIndex:30},children:e.jsxs("picture",{children:[e.jsx("source",{srcSet:window.origin+"/cloud/speakid/games/whatsmissing/logo.svg",type:"image/svg+xml"}),e.jsx("img",{src:window.origin+"/cloud/speakid/games/whatsmissing/logo.png",alt:"SPEAKID Logo",style:n.gmLogoImg,loading:"lazy"})]})}),[]);return e.jsx("div",{ref:d,style:{position:"fixed",inset:0,display:"flex",justifyContent:"center",alignItems:"center",background:"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",transition:"background 0.3s ease",overflow:"hidden",zIndex:9999,pointerEvents:"auto"},children:e.jsx("div",{ref:f,style:{width:b?"100%":I?`${I}px`:"1000px",height:b?"100%":I?`${I}px`:"1000px",maxWidth:"calc(100vw - 24px)",maxHeight:"calc(100vh - 24px)",display:"flex",justifyContent:"center",alignItems:"center",overflow:"hidden",borderRadius:b?0:"20px",background:"linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",boxShadow:b?"none":"0 0 40px rgba(0,0,0,0.08)",position:"relative"},children:e.jsx("div",{style:{transform:"none",transformOrigin:"50% 50%",width:"100%",height:"100%",display:"flex",justifyContent:"center",alignItems:"center"},children:e.jsxs("div",{id:"whats-missing-root",children:[!b&&ce,!m&&!x&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsx("h1",{style:n.gmHeadline1,children:"WHAT'S MISSING?"}),e.jsx("p",{style:n.gmBodyM,children:"Select a theme:"}),e.jsx("div",{style:{display:"flex",gap:16},children:["animals","food","toys"].map(t=>e.jsx("button",{style:n.gmButton,onClick:()=>l(t),children:t==="animals"?"🐶 Animals":t==="food"?"🍎 Food":"🧸 Toys"},t))}),e.jsxs("div",{style:{marginTop:24},children:[e.jsx("p",{style:n.gmBodyS,children:"Choose number of rounds:"}),e.jsx("div",{style:{display:"flex",gap:12,marginTop:8},children:[3,4,5].map(t=>e.jsx("button",{style:{...n.gmButton,...j===t?n.gmButtonActive:{}},onClick:()=>M(t),children:t},t))})]})]}),m&&!x&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsxs("h1",{style:n.gmHeadline1,children:["Theme selected: ",m]}),e.jsxs("p",{style:n.gmBodyM,children:["Rounds: ",j]}),e.jsx("button",{style:n.gmButton,onClick:ee,children:"▶ Start game"})]}),H&&e.jsxs("div",{style:n.gmCenterScreen,children:[e.jsx("h1",{style:n.gmHeadline1,children:"Results"}),e.jsxs("h2",{style:n.gmHeadline3,children:["Your score: ",oe," / ",j]}),e.jsx("p",{style:{...n.gmBodyM,color:"#10b981",marginTop:12},children:"Yahoo! You did it! 🍬✨"}),e.jsxs("table",{style:n.gmTable,children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:"Round"}),e.jsx("th",{children:"Your Answer"}),e.jsx("th",{children:"Correct"}),e.jsx("th",{children:"Result"})]})}),e.jsx("tbody",{children:se.map((t,o)=>e.jsxs("tr",{children:[e.jsx("td",{style:n.gmTableCell,children:t.round}),e.jsx("td",{style:n.gmTableCell,children:t.answer||"—"}),e.jsx("td",{style:n.gmTableCell,children:t.correct}),e.jsx("td",{style:n.gmTableCell,children:t.result==="correct"?"✔ Correct":t.result==="almost"?"◐ Almost (0.5)":"✘ Wrong"})]},o))})]}),e.jsxs("div",{style:{display:"flex",gap:12,marginTop:24},children:[e.jsx("button",{style:n.gmButton,onClick:ee,children:"🔁 Play again"}),e.jsx("button",{style:n.gmButton,onClick:de,children:"⬅️ Choose theme"})]})]}),x&&!H&&e.jsxs("div",{style:n.gmGameLayout,children:[e.jsxs("div",{style:{minHeight:160,display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:[c==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("h1",{style:{...n.gmHeadline1,color:"#ec4c44"},children:"GET READY"}),e.jsx("div",{style:n.gmHourglass,children:"⏳"})]}),c==="memorize"&&e.jsxs("p",{style:{...n.gmBodyM,color:"#10b981"},children:["MEMORIZE (",A,")"]}),c==="guess"&&!R&&e.jsxs("p",{style:n.gmBodyM,children:["⏳ Time left: ",F,"s"]})]}),c!=="ready"&&(()=>{const t=b?12:20,o=32,u=g??I??(b?360:1e3),a=b?2:3,h=Math.max(90,Math.floor((u-o-t*(a-1))/a));return e.jsx("div",{style:{...n.gmGrid,gridTemplateColumns:`repeat(${a}, ${h}px)`,gridAutoRows:`${h}px`,gap:`${t}px`,justifyContent:"center",justifyItems:"center",padding:8,boxSizing:"border-box"},children:z.map((w,v)=>{const P=p===v&&c==="guess"&&!R;return e.jsx("div",{style:{...n.gmCard,width:`${h}px`,height:`${h}px`,padding:b?8:10,boxSizing:"border-box",...K==="correct"&&p===v?n.gmCorrect:{},...K==="wrong"&&p===v?n.gmWrong:{}},children:!P&&e.jsx("img",{src:w.src,alt:w.name,style:{width:"100%",height:"100%",objectFit:"cover",display:"block"}})},v)})})})(),e.jsxs("div",{style:{marginTop:16,height:80},children:[c==="guess"&&!R&&e.jsxs("div",{children:[e.jsx("input",{type:"text",placeholder:"Type the missing word",value:B,onChange:t=>_(t.target.value),style:n.gmInput}),e.jsx("button",{style:{...n.gmButton,marginLeft:8},onClick:le,disabled:G,children:G?"...":"Check"})]}),R&&e.jsx("button",{type:"button",style:n.gmButton,onClick:ae,children:"Next round"})]})]})]})})})})}exports.Game=Ce;exports.themes=V;
@@ -1,9 +1,9 @@
1
- import { jsx as t, jsxs as c, Fragment as ue } from "react/jsx-runtime";
2
- import { useRef as te, useEffect as P, useState as d, useMemo as he, useLayoutEffect as fe } from "react";
3
- const pe = window.origin + "/cloud/speakid/games/whatsmissing/", V = (l, g) => l.map(([m, o]) => ({
4
- src: `${pe}${g}/${o.replace(/\s/g, "%20")}.png`,
5
- name: o
6
- })), ye = [
1
+ import { jsx as t, jsxs as m, Fragment as we } from "react/jsx-runtime";
2
+ import { useRef as Z, useState as c, useEffect as V, useLayoutEffect as se, useMemo as ve } from "react";
3
+ const ze = window.origin + "/cloud/speakid/games/whatsmissing/", Y = (a, f) => a.map(([h, i]) => ({
4
+ src: `${ze}${f}/${i.replace(/\s/g, "%20")}.png`,
5
+ name: i
6
+ })), Ce = [
7
7
  ["apple", "apple"],
8
8
  ["banana", "banana"],
9
9
  ["bread", "bread"],
@@ -29,7 +29,7 @@ const pe = window.origin + "/cloud/speakid/games/whatsmissing/", V = (l, g) => l
29
29
  ["strawberry", "strawberry"],
30
30
  ["tomato", "tomato"],
31
31
  ["watermelon", "watermelon"]
32
- ], be = [
32
+ ], ke = [
33
33
  ["bear", "bear"],
34
34
  ["cat", "cat"],
35
35
  ["crocodile", "crocodile"],
@@ -49,7 +49,7 @@ const pe = window.origin + "/cloud/speakid/games/whatsmissing/", V = (l, g) => l
49
49
  ["tortoise", "tortoise"],
50
50
  ["wolf", "wolf"],
51
51
  ["zebra", "zebra"]
52
- ], we = [
52
+ ], Se = [
53
53
  ["ball", "ball"],
54
54
  ["balloon", "balloon"],
55
55
  ["blocks", "blocks"],
@@ -69,21 +69,21 @@ const pe = window.origin + "/cloud/speakid/games/whatsmissing/", V = (l, g) => l
69
69
  ["teddy_bear", "teddy bear"],
70
70
  ["yo-yo", "yo-yo"],
71
71
  ["crayons", "crayons"]
72
- ], ee = {
73
- food: V(ye, "food"),
74
- animals: V(be, "animals"),
75
- toys: V(we, "toys")
76
- }, xe = `
72
+ ], re = {
73
+ food: Y(Ce, "food"),
74
+ animals: Y(ke, "animals"),
75
+ toys: Y(Se, "toys")
76
+ }, Me = `
77
77
  @keyframes spin {
78
78
  from { transform: rotate(0deg); }
79
79
  to { transform: rotate(360deg); }
80
80
  }
81
81
  `;
82
82
  if (typeof document < "u" && !document.getElementById("spin-keyframes")) {
83
- const l = document.createElement("style");
84
- l.id = "spin-keyframes", l.innerHTML = xe, document.head.appendChild(l);
83
+ const a = document.createElement("style");
84
+ a.id = "spin-keyframes", a.innerHTML = Me, document.head.appendChild(a);
85
85
  }
86
- const ve = {
86
+ const Te = {
87
87
  animation: "spin 1.4s linear infinite"
88
88
  }, n = {
89
89
  gmCenterScreen: {
@@ -236,159 +236,175 @@ const ve = {
236
236
  },
237
237
  gmHourglass: {
238
238
  fontSize: "42px",
239
- ...ve
239
+ ...Te
240
240
  }
241
- }, ke = () => {
242
- const l = document.createElement("style");
243
- l.textContent = `
241
+ }, Re = () => {
242
+ const a = document.createElement("style");
243
+ a.textContent = `
244
244
  /* scope only inside game root */
245
245
  #whats-missing-root, #whats-missing-root * { box-sizing: border-box; }
246
246
  #whats-missing-root img { max-width:100%; height:auto; display:block; user-select:none; }
247
- `, document.head.appendChild(l);
248
- }, ze = (l, g) => {
249
- const m = Array.from({ length: l.length + 1 }, () => Array(g.length + 1).fill(0));
250
- for (let o = 0; o <= l.length; o++) m[o][0] = o;
251
- for (let o = 0; o <= g.length; o++) m[0][o] = o;
252
- for (let o = 1; o <= l.length; o++)
253
- for (let u = 1; u <= g.length; u++)
254
- m[o][u] = l[o - 1] === g[u - 1] ? m[o - 1][u - 1] : Math.min(m[o - 1][u - 1], m[o][u - 1], m[o - 1][u]) + 1;
255
- return m[l.length][g.length];
247
+ `, document.head.appendChild(a);
248
+ }, Ee = (a, f) => {
249
+ const h = Array.from({ length: a.length + 1 }, () => Array(f.length + 1).fill(0));
250
+ for (let i = 0; i <= a.length; i++) h[i][0] = i;
251
+ for (let i = 0; i <= f.length; i++) h[0][i] = i;
252
+ for (let i = 1; i <= a.length; i++)
253
+ for (let u = 1; u <= f.length; u++)
254
+ h[i][u] = a[i - 1] === f[u - 1] ? h[i - 1][u - 1] : Math.min(h[i - 1][u - 1], h[i][u - 1], h[i - 1][u]) + 1;
255
+ return h[a.length][f.length];
256
256
  };
257
- function Ce(l, g = { minDesktopWidth: 1200, forceResize: !1 }) {
258
- const m = te(null), o = g.minDesktopWidth ?? 1200, u = !!g.forceResize;
259
- fe(() => {
260
- if (typeof window > "u" || !l || !l.current) return;
261
- const s = l.current, F = () => {
257
+ function Ie(a, f = { minDesktopWidth: 1200, forceResize: !1 }) {
258
+ const h = Z(null), i = f.minDesktopWidth ?? 1200, u = !!f.forceResize;
259
+ se(() => {
260
+ if (typeof window > "u" || !a || !a.current) return;
261
+ const s = a.current, S = () => {
262
262
  try {
263
- const i = document.body, a = getComputedStyle(i), y = parseFloat(i.style && i.style.zoom || "");
263
+ const r = document.body, b = getComputedStyle(r), y = parseFloat(r.style && r.style.zoom || "");
264
264
  if (y && !isNaN(y) && y > 0) return y;
265
- const k = parseFloat(a.zoom || "");
266
- if (k && !isNaN(k) && k > 0) return k;
267
- const S = a.transform || a.webkitTransform || a.mozTransform;
268
- if (S && S !== "none") {
269
- const T = S.match(/matrix\(([^)]+)\)/);
270
- if (T) {
271
- const z = T[1].split(",").map((E) => parseFloat(E))[0];
272
- if (z && !isNaN(z)) return z;
273
- }
265
+ const M = parseFloat(b.zoom || "");
266
+ if (M && !isNaN(M) && M > 0) return M;
267
+ const d = document.createElement("div");
268
+ d.style.position = "absolute", d.style.left = "0", d.style.top = "0", d.style.width = "100px", d.style.height = "1px", d.style.visibility = "hidden", d.style.pointerEvents = "none", document.documentElement.appendChild(d);
269
+ const k = d.getBoundingClientRect();
270
+ if (document.documentElement.removeChild(d), k && k.width > 0) {
271
+ const T = k.width / 100;
272
+ if (!isNaN(T) && T > 0) return T;
274
273
  }
275
274
  } catch {
276
275
  }
277
276
  return 1;
278
- }, x = () => {
279
- if (o && window.innerWidth < o) {
277
+ }, B = () => {
278
+ if (i && window.innerWidth < i) {
280
279
  C();
281
280
  return;
282
281
  }
283
- const i = F();
284
- if (!i || i === 1) {
282
+ const r = S();
283
+ if (!r || r === 1) {
285
284
  C();
286
285
  return;
287
286
  }
288
- m.current || (m.current = {
287
+ h.current || (h.current = {
289
288
  transform: s.style.transform || null,
290
289
  transformOrigin: s.style.transformOrigin || null,
291
290
  width: s.style.width || null,
292
291
  height: s.style.height || null,
293
292
  willChange: s.style.willChange || null
294
293
  });
295
- const a = 1 / i;
296
- u && (s.style.width = `${i * 100}%`, s.style.height = `${i * 100}%`), s.style.transform = `scale(${a})`, s.style.transformOrigin = "top left", s.style.willChange = "transform", typeof process < "u" && process.env && process.env.NODE_ENV === "development" && console.debug("[useNeutralizeBodyZoom] applied scale:", { scale: i, inv: a, forceResize: u });
294
+ const b = 1 / r;
295
+ u && (s.style.width = `${r * 100}%`, s.style.height = `${r * 100}%`), s.style.transformOrigin = "50% 50%", s.style.transform = `scale(${b})`, s.style.willChange = "transform", typeof process < "u" && process.env?.NODE_ENV === "development" && console.debug("[useNeutralizeBodyZoom] applied scale:", { scale: r, inv: b, forceResize: u });
297
296
  }, C = () => {
298
- if (!m.current) return;
299
- const i = m.current;
300
- i.transform === null ? s.style.removeProperty("transform") : s.style.transform = i.transform, i.transformOrigin === null ? s.style.removeProperty("transform-origin") : s.style.transformOrigin = i.transformOrigin, i.width === null ? s.style.removeProperty("width") : s.style.width = i.width, i.height === null ? s.style.removeProperty("height") : s.style.height = i.height, i.willChange === null ? s.style.removeProperty("will-change") : s.style.willChange = i.willChange, m.current = null, process.env.NODE_ENV === "development" && console.debug("[useNeutralizeBodyZoom] restored original styles");
297
+ if (!h.current) return;
298
+ const r = h.current;
299
+ r.transform === null ? s.style.removeProperty("transform") : s.style.transform = r.transform, r.transformOrigin === null ? s.style.removeProperty("transform-origin") : s.style.transformOrigin = r.transformOrigin, r.width === null ? s.style.removeProperty("width") : s.style.width = r.width, r.height === null ? s.style.removeProperty("height") : s.style.height = r.height, r.willChange === null ? s.style.removeProperty("will-change") : s.style.willChange = r.willChange, h.current = null, typeof process < "u" && process.env?.NODE_ENV === "development" && console.debug("[useNeutralizeBodyZoom] restored original styles");
301
300
  };
302
- let w = 0;
303
- const v = () => {
304
- cancelAnimationFrame(w), w = requestAnimationFrame(x);
305
- }, p = new MutationObserver((i) => {
306
- for (const a of i)
307
- if (a.type === "attributes" && (a.attributeName === "style" || a.attributeName === "class")) {
308
- v();
301
+ let E = 0;
302
+ const x = () => {
303
+ cancelAnimationFrame(E), E = requestAnimationFrame(B);
304
+ }, I = new MutationObserver((r) => {
305
+ for (const b of r)
306
+ if (b.type === "attributes" && (b.attributeName === "style" || b.attributeName === "class")) {
307
+ x();
309
308
  break;
310
309
  }
311
310
  });
312
- return p.observe(document.body, { attributes: !0, attributeFilter: ["style", "class"] }), window.addEventListener("resize", v), v(), () => {
313
- cancelAnimationFrame(w), p.disconnect(), window.removeEventListener("resize", v), C();
311
+ return I.observe(document.body, { attributes: !0, attributeFilter: ["style", "class"] }), window.addEventListener("resize", x), x(), () => {
312
+ cancelAnimationFrame(E), I.disconnect(), window.removeEventListener("resize", x), C();
314
313
  };
315
- }, [l, o, u]);
314
+ }, [a, i, u]);
316
315
  }
317
- function Me() {
318
- const l = te(null);
319
- Ce(l, { minDesktopWidth: 1200, forceResize: !1 }), P(() => (ke(), () => {
316
+ function Be() {
317
+ const a = Z(null), f = Z(null), [h, i] = c(null);
318
+ Ie(a, { minDesktopWidth: 1200, forceResize: !1 }), V(() => (Re(), () => {
320
319
  document.body.style.overflow = "";
321
320
  }), []);
322
- const [g, m] = d(null), [o, u] = d([]), [s, F] = d(4), [x, C] = d(!1), [w, v] = d(1), [p, i] = d(null), [a, y] = d("ready"), [k, S] = d(3), [T, B] = d(10), [z, E] = d(20), [ne, W] = d(0), [A, N] = d(!1), [H, Y] = d(""), [R, j] = d(!1), [D, $] = d(!1), [q, L] = d(null), [oe, G] = d([]), [re, Z] = d([]), [b, ie] = d(!1), [Se, _] = d(1), [U, K] = d(null);
323
- P(() => {
321
+ const [u, s] = c(null), [S, B] = c([]), [C, E] = c(4), [x, I] = c(!1), [r, b] = c(1), [y, M] = c(null), [d, k] = c("ready"), [T, _] = c(3), [j, F] = c(10), [N, U] = c(20), [le, $] = c(0), [A, O] = c(!1), [W, K] = c(""), [R, G] = c(!1), [D, J] = c(!1), [Q, H] = c(null), [ae, P] = c([]), [de, X] = c([]), [w, ce] = c(!1), [L, ee] = c(null);
322
+ se(() => {
323
+ const e = f.current;
324
+ if (!e || typeof window > "u") return;
325
+ let o = null;
326
+ try {
327
+ o = new ResizeObserver((g) => {
328
+ for (const l of g) {
329
+ const p = l.contentRect;
330
+ i(Math.round(Math.min(p.width, p.height)));
331
+ }
332
+ }), o.observe(e), i(Math.round(Math.min(e.clientWidth, e.clientHeight)));
333
+ } catch {
334
+ i(Math.round(Math.min(e.clientWidth, e.clientHeight)));
335
+ }
336
+ return () => {
337
+ o && o.disconnect();
338
+ };
339
+ }, [w]), V(() => {
324
340
  const e = () => {
325
- const h = window.visualViewport, f = h ? Math.round(h.width) : window.innerWidth, I = h ? Math.round(h.height) : window.innerHeight, M = f < 768;
326
- if (ie(M), M)
327
- K(null), _(1);
328
- else {
329
- const ce = Math.min(f, I), me = Math.max(360, Math.round(ce * 0.97)), ge = Math.min(me, 1e3);
330
- K(ge), _(1);
341
+ const g = window.visualViewport, l = g ? Math.round(g.width) : window.innerWidth, p = g ? Math.round(g.height) : window.innerHeight, v = l < 768 || l >= 768 && l < 1200 && p > l;
342
+ if (ce(v), v) {
343
+ ee(null);
344
+ return;
331
345
  }
346
+ const z = 0.92, q = 1e3, ie = 40, pe = Math.max(360, l - ie), fe = Math.max(360, p - ie), ye = Math.min(pe, fe), be = Math.max(360, Math.round(ye * z)), xe = Math.min(be, q);
347
+ ee(xe);
332
348
  };
333
349
  e(), window.addEventListener("resize", e);
334
- const r = window.visualViewport;
335
- return r && typeof r.addEventListener == "function" && (r.addEventListener("resize", e), r.addEventListener("scroll", e)), () => {
336
- window.removeEventListener("resize", e), r && typeof r.removeEventListener == "function" && (r.removeEventListener("resize", e), r.removeEventListener("scroll", e));
350
+ const o = window.visualViewport;
351
+ return o && typeof o.addEventListener == "function" && (o.addEventListener("resize", e), o.addEventListener("scroll", e)), () => {
352
+ window.removeEventListener("resize", e), o && typeof o.removeEventListener == "function" && (o.removeEventListener("resize", e), o.removeEventListener("scroll", e));
337
353
  };
338
354
  }, []);
339
- const X = (e) => [...e].sort(() => Math.random() - 0.5).slice(0, 6), J = () => {
340
- if (!g) return;
341
- const e = X(ee[g]);
342
- u(e), C(!0), N(!1), v(1), W(0), G([]), Z([]), Q(e, [], !0);
343
- }, Q = (e = o, r = re, h = !1) => {
344
- const f = X(e.length ? e : ee[g]);
345
- let I = Math.floor(Math.random() * f.length), M = f[I].name, O = 0;
346
- for (; r.includes(M) && O < 20; )
347
- I = Math.floor(Math.random() * f.length), M = f[I].name, O++;
348
- u(f), i(I), Z([...r, M]), j(!1), Y(""), E(20), L(null), h ? (y("ready"), S(3), B(10)) : (y("memorize"), B(10));
355
+ const te = (e) => [...e].sort(() => Math.random() - 0.5).slice(0, 6), ne = () => {
356
+ if (!u) return;
357
+ const e = te(re[u]);
358
+ B(e), I(!0), O(!1), b(1), $(0), P([]), X([]), oe(e, [], !0);
359
+ }, oe = (e = S, o = de, g = !1) => {
360
+ const l = te(e.length ? e : re[u]);
361
+ let p = Math.floor(Math.random() * l.length), v = l[p].name, z = 0;
362
+ for (; o.includes(v) && z < 20; )
363
+ p = Math.floor(Math.random() * l.length), v = l[p].name, z++;
364
+ B(l), M(p), X([...o, v]), G(!1), K(""), U(20), H(null), g ? (k("ready"), _(3), F(10)) : (k("memorize"), F(10));
349
365
  };
350
- P(() => {
366
+ V(() => {
351
367
  if (!(!x || A || R)) {
352
- if (a === "ready")
353
- if (k <= 0) y("memorize");
368
+ if (d === "ready")
369
+ if (T <= 0) k("memorize");
354
370
  else {
355
- const e = setTimeout(() => S((r) => r - 1), 1e3);
371
+ const e = setTimeout(() => _((o) => o - 1), 1e3);
356
372
  return () => clearTimeout(e);
357
373
  }
358
- if (a === "memorize")
359
- if (T <= 0) y("guess");
374
+ if (d === "memorize")
375
+ if (j <= 0) k("guess");
360
376
  else {
361
- const e = setTimeout(() => B((r) => r - 1), 1e3);
377
+ const e = setTimeout(() => F((o) => o - 1), 1e3);
362
378
  return () => clearTimeout(e);
363
379
  }
364
- if (a === "guess") {
365
- if (z <= 0) {
366
- j(!0), L("wrong");
367
- const r = o[p].name;
368
- G((h) => [
369
- ...h,
370
- { round: w, answer: H, correct: r, result: "wrong" }
380
+ if (d === "guess") {
381
+ if (N <= 0) {
382
+ G(!0), H("wrong");
383
+ const o = S[y].name;
384
+ P((g) => [
385
+ ...g,
386
+ { round: r, answer: W, correct: o, result: "wrong" }
371
387
  ]);
372
388
  return;
373
389
  }
374
- const e = setTimeout(() => E((r) => r - 1), 1e3);
390
+ const e = setTimeout(() => U((o) => o - 1), 1e3);
375
391
  return () => clearTimeout(e);
376
392
  }
377
393
  }
378
- }, [a, k, T, z, x, A, R, o, p, w, H]);
379
- const se = () => {
380
- if (p === null || D) return;
381
- const e = o[p].name, r = H.toLowerCase().trim();
382
- let h = "wrong";
383
- r === e ? (W((f) => f + 1), L("correct"), h = "correct") : ze(r, e) === 1 ? (W((f) => f + 0.5), L("almost"), h = "almost") : (L("wrong"), h = "wrong"), G((f) => [...f, { round: w, answer: r, correct: e, result: h }]), $(!0), setTimeout(() => {
384
- $(!1), j(!0);
394
+ }, [d, T, j, N, x, A, R, S, y, r, W]);
395
+ const me = () => {
396
+ if (y === null || D) return;
397
+ const e = S[y].name, o = W.toLowerCase().trim();
398
+ let g = "wrong";
399
+ o === e ? ($((l) => l + 1), H("correct"), g = "correct") : Ee(o, e) === 1 ? ($((l) => l + 0.5), H("almost"), g = "almost") : (H("wrong"), g = "wrong"), P((l) => [...l, { round: r, answer: o, correct: e, result: g }]), J(!0), setTimeout(() => {
400
+ J(!1), G(!0);
385
401
  }, 600);
386
- }, le = () => w < s ? (v((e) => e + 1), Q()) : N(!0), ae = () => {
387
- C(!1), N(!1), m(null);
388
- }, de = he(
402
+ }, ue = () => r < C ? (b((e) => e + 1), oe()) : O(!0), ge = () => {
403
+ I(!1), O(!1), s(null);
404
+ }, he = ve(
389
405
  () => (
390
406
  // ensure logo is positioned inside the square container
391
- /* @__PURE__ */ t("div", { style: { ...n.gmLogoFixed, position: "absolute", top: 16, left: 16, zIndex: 30 }, children: /* @__PURE__ */ c("picture", { children: [
407
+ /* @__PURE__ */ t("div", { style: { ...n.gmLogoFixed, position: "absolute", top: 16, left: 16, zIndex: 30 }, children: /* @__PURE__ */ m("picture", { children: [
392
408
  /* @__PURE__ */ t("source", { srcSet: window.origin + "/cloud/speakid/games/whatsmissing/logo.svg", type: "image/svg+xml" }),
393
409
  /* @__PURE__ */ t("img", { src: window.origin + "/cloud/speakid/games/whatsmissing/logo.png", alt: "SPEAKID Logo", style: n.gmLogoImg, loading: "lazy" })
394
410
  ] }) })
@@ -398,108 +414,113 @@ function Me() {
398
414
  return /* @__PURE__ */ t(
399
415
  "div",
400
416
  {
401
- ref: l,
417
+ ref: a,
402
418
  style: {
403
- width: "100%",
404
- height: "100vh",
419
+ // center relative to viewport so site layout doesn't push the game off-center
420
+ position: "fixed",
421
+ inset: 0,
405
422
  display: "flex",
406
423
  justifyContent: "center",
407
424
  alignItems: "center",
408
425
  background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
409
426
  transition: "background 0.3s ease",
410
- overflowX: "clip"
427
+ overflow: "hidden",
428
+ zIndex: 9999,
429
+ pointerEvents: "auto"
411
430
  },
412
431
  children: /* @__PURE__ */ t(
413
432
  "div",
414
433
  {
434
+ ref: f,
415
435
  style: {
416
- width: b ? "100%" : U || 1e3,
417
- height: b ? "100%" : U || 1e3,
436
+ width: w ? "100%" : L ? `${L}px` : "1000px",
437
+ height: w ? "100%" : L ? `${L}px` : "1000px",
438
+ maxWidth: "calc(100vw - 24px)",
439
+ maxHeight: "calc(100vh - 24px)",
418
440
  display: "flex",
419
441
  justifyContent: "center",
420
442
  alignItems: "center",
421
443
  overflow: "hidden",
422
- borderRadius: b ? 0 : "20px",
444
+ borderRadius: w ? 0 : "20px",
423
445
  background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
424
- boxShadow: b ? "none" : "0 0 40px rgba(0,0,0,0.1)",
425
- margin: b ? "0 auto" : "unset",
446
+ boxShadow: w ? "none" : "0 0 40px rgba(0,0,0,0.08)",
426
447
  position: "relative"
427
- // needed so absolute logo is inside the square
428
448
  },
429
449
  children: /* @__PURE__ */ t(
430
450
  "div",
431
451
  {
432
452
  style: {
433
453
  transform: "none",
454
+ transformOrigin: "50% 50%",
434
455
  width: "100%",
435
456
  height: "100%",
436
457
  display: "flex",
437
458
  justifyContent: "center",
438
459
  alignItems: "center"
439
460
  },
440
- children: /* @__PURE__ */ c("div", { id: "whats-missing-root", children: [
441
- !b && de,
442
- !g && !x && /* @__PURE__ */ c("div", { style: n.gmCenterScreen, children: [
461
+ children: /* @__PURE__ */ m("div", { id: "whats-missing-root", children: [
462
+ !w && he,
463
+ !u && !x && /* @__PURE__ */ m("div", { style: n.gmCenterScreen, children: [
443
464
  /* @__PURE__ */ t("h1", { style: n.gmHeadline1, children: "WHAT'S MISSING?" }),
444
465
  /* @__PURE__ */ t("p", { style: n.gmBodyM, children: "Select a theme:" }),
445
- /* @__PURE__ */ t("div", { style: { display: "flex", gap: 16 }, children: ["animals", "food", "toys"].map((e) => /* @__PURE__ */ t("button", { style: n.gmButton, onClick: () => m(e), children: e === "animals" ? "🐶 Animals" : e === "food" ? "🍎 Food" : "🧸 Toys" }, e)) }),
446
- /* @__PURE__ */ c("div", { style: { marginTop: 24 }, children: [
466
+ /* @__PURE__ */ t("div", { style: { display: "flex", gap: 16 }, children: ["animals", "food", "toys"].map((e) => /* @__PURE__ */ t("button", { style: n.gmButton, onClick: () => s(e), children: e === "animals" ? "🐶 Animals" : e === "food" ? "🍎 Food" : "🧸 Toys" }, e)) }),
467
+ /* @__PURE__ */ m("div", { style: { marginTop: 24 }, children: [
447
468
  /* @__PURE__ */ t("p", { style: n.gmBodyS, children: "Choose number of rounds:" }),
448
469
  /* @__PURE__ */ t("div", { style: { display: "flex", gap: 12, marginTop: 8 }, children: [3, 4, 5].map((e) => /* @__PURE__ */ t(
449
470
  "button",
450
471
  {
451
472
  style: {
452
473
  ...n.gmButton,
453
- ...s === e ? n.gmButtonActive : {}
474
+ ...C === e ? n.gmButtonActive : {}
454
475
  },
455
- onClick: () => F(e),
476
+ onClick: () => E(e),
456
477
  children: e
457
478
  },
458
479
  e
459
480
  )) })
460
481
  ] })
461
482
  ] }),
462
- g && !x && /* @__PURE__ */ c("div", { style: n.gmCenterScreen, children: [
463
- /* @__PURE__ */ c("h1", { style: n.gmHeadline1, children: [
483
+ u && !x && /* @__PURE__ */ m("div", { style: n.gmCenterScreen, children: [
484
+ /* @__PURE__ */ m("h1", { style: n.gmHeadline1, children: [
464
485
  "Theme selected: ",
465
- g
486
+ u
466
487
  ] }),
467
- /* @__PURE__ */ c("p", { style: n.gmBodyM, children: [
488
+ /* @__PURE__ */ m("p", { style: n.gmBodyM, children: [
468
489
  "Rounds: ",
469
- s
490
+ C
470
491
  ] }),
471
- /* @__PURE__ */ t("button", { style: n.gmButton, onClick: J, children: "▶ Start game" })
492
+ /* @__PURE__ */ t("button", { style: n.gmButton, onClick: ne, children: "▶ Start game" })
472
493
  ] }),
473
- A && /* @__PURE__ */ c("div", { style: n.gmCenterScreen, children: [
494
+ A && /* @__PURE__ */ m("div", { style: n.gmCenterScreen, children: [
474
495
  /* @__PURE__ */ t("h1", { style: n.gmHeadline1, children: "Results" }),
475
- /* @__PURE__ */ c("h2", { style: n.gmHeadline3, children: [
496
+ /* @__PURE__ */ m("h2", { style: n.gmHeadline3, children: [
476
497
  "Your score: ",
477
- ne,
498
+ le,
478
499
  " / ",
479
- s
500
+ C
480
501
  ] }),
481
502
  /* @__PURE__ */ t("p", { style: { ...n.gmBodyM, color: "#10b981", marginTop: 12 }, children: "Yahoo! You did it! 🍬✨" }),
482
- /* @__PURE__ */ c("table", { style: n.gmTable, children: [
483
- /* @__PURE__ */ t("thead", { children: /* @__PURE__ */ c("tr", { children: [
503
+ /* @__PURE__ */ m("table", { style: n.gmTable, children: [
504
+ /* @__PURE__ */ t("thead", { children: /* @__PURE__ */ m("tr", { children: [
484
505
  /* @__PURE__ */ t("th", { children: "Round" }),
485
506
  /* @__PURE__ */ t("th", { children: "Your Answer" }),
486
507
  /* @__PURE__ */ t("th", { children: "Correct" }),
487
508
  /* @__PURE__ */ t("th", { children: "Result" })
488
509
  ] }) }),
489
- /* @__PURE__ */ t("tbody", { children: oe.map((e, r) => /* @__PURE__ */ c("tr", { children: [
510
+ /* @__PURE__ */ t("tbody", { children: ae.map((e, o) => /* @__PURE__ */ m("tr", { children: [
490
511
  /* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.round }),
491
512
  /* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.answer || "—" }),
492
513
  /* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.correct }),
493
514
  /* @__PURE__ */ t("td", { style: n.gmTableCell, children: e.result === "correct" ? "✔ Correct" : e.result === "almost" ? "◐ Almost (0.5)" : "✘ Wrong" })
494
- ] }, r)) })
515
+ ] }, o)) })
495
516
  ] }),
496
- /* @__PURE__ */ c("div", { style: { display: "flex", gap: 12, marginTop: 24 }, children: [
497
- /* @__PURE__ */ t("button", { style: n.gmButton, onClick: J, children: "🔁 Play again" }),
498
- /* @__PURE__ */ t("button", { style: n.gmButton, onClick: ae, children: "⬅️ Choose theme" })
517
+ /* @__PURE__ */ m("div", { style: { display: "flex", gap: 12, marginTop: 24 }, children: [
518
+ /* @__PURE__ */ t("button", { style: n.gmButton, onClick: ne, children: "🔁 Play again" }),
519
+ /* @__PURE__ */ t("button", { style: n.gmButton, onClick: ge, children: "⬅️ Choose theme" })
499
520
  ] })
500
521
  ] }),
501
- x && !A && /* @__PURE__ */ c("div", { style: n.gmGameLayout, children: [
502
- /* @__PURE__ */ c(
522
+ x && !A && /* @__PURE__ */ m("div", { style: n.gmGameLayout, children: [
523
+ /* @__PURE__ */ m(
503
524
  "div",
504
525
  {
505
526
  style: {
@@ -510,70 +531,79 @@ function Me() {
510
531
  alignItems: "center"
511
532
  },
512
533
  children: [
513
- a === "ready" && /* @__PURE__ */ c(ue, { children: [
534
+ d === "ready" && /* @__PURE__ */ m(we, { children: [
514
535
  /* @__PURE__ */ t("h1", { style: { ...n.gmHeadline1, color: "#ec4c44" }, children: "GET READY" }),
515
536
  /* @__PURE__ */ t("div", { style: n.gmHourglass, children: "⏳" })
516
537
  ] }),
517
- a === "memorize" && /* @__PURE__ */ c("p", { style: { ...n.gmBodyM, color: "#10b981" }, children: [
538
+ d === "memorize" && /* @__PURE__ */ m("p", { style: { ...n.gmBodyM, color: "#10b981" }, children: [
518
539
  "MEMORIZE (",
519
- T,
540
+ j,
520
541
  ")"
521
542
  ] }),
522
- a === "guess" && !R && /* @__PURE__ */ c("p", { style: n.gmBodyM, children: [
543
+ d === "guess" && !R && /* @__PURE__ */ m("p", { style: n.gmBodyM, children: [
523
544
  "⏳ Time left: ",
524
- z,
545
+ N,
525
546
  "s"
526
547
  ] })
527
548
  ]
528
549
  }
529
550
  ),
530
- a !== "ready" && /* @__PURE__ */ t(
531
- "div",
532
- {
533
- style: {
534
- ...n.gmGrid,
535
- gridTemplateColumns: b ? "repeat(2, 1fr)" : "repeat(3, 210px)",
536
- gridAutoRows: b ? "150px" : "210px",
537
- gap: b ? "12px" : "20px",
538
- justifyItems: "center"
539
- },
540
- children: o.map((e, r) => {
541
- const h = p === r && a === "guess" && !R;
542
- return /* @__PURE__ */ t(
543
- "div",
544
- {
545
- style: {
546
- ...n.gmCard,
547
- ...q === "correct" && p === r ? n.gmCorrect : {},
548
- ...q === "wrong" && p === r ? n.gmWrong : {}
549
- },
550
- children: !h && /* @__PURE__ */ t(
551
- "img",
552
- {
553
- src: e.src,
554
- alt: e.name,
555
- style: {
556
- width: "100%",
557
- height: "100%",
558
- objectFit: "cover"
551
+ d !== "ready" && (() => {
552
+ const e = w ? 12 : 20, o = 32, g = h ?? L ?? (w ? 360 : 1e3), l = w ? 2 : 3, p = Math.max(
553
+ 90,
554
+ Math.floor((g - o - e * (l - 1)) / l)
555
+ );
556
+ return /* @__PURE__ */ t(
557
+ "div",
558
+ {
559
+ style: {
560
+ ...n.gmGrid,
561
+ gridTemplateColumns: `repeat(${l}, ${p}px)`,
562
+ gridAutoRows: `${p}px`,
563
+ gap: `${e}px`,
564
+ justifyContent: "center",
565
+ justifyItems: "center",
566
+ padding: 8,
567
+ boxSizing: "border-box"
568
+ },
569
+ children: S.map((v, z) => {
570
+ const q = y === z && d === "guess" && !R;
571
+ return /* @__PURE__ */ t(
572
+ "div",
573
+ {
574
+ style: {
575
+ ...n.gmCard,
576
+ width: `${p}px`,
577
+ height: `${p}px`,
578
+ padding: w ? 8 : 10,
579
+ boxSizing: "border-box",
580
+ ...Q === "correct" && y === z ? n.gmCorrect : {},
581
+ ...Q === "wrong" && y === z ? n.gmWrong : {}
582
+ },
583
+ children: !q && /* @__PURE__ */ t(
584
+ "img",
585
+ {
586
+ src: v.src,
587
+ alt: v.name,
588
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
559
589
  }
560
- }
561
- )
562
- },
563
- r
564
- );
565
- })
566
- }
567
- ),
568
- /* @__PURE__ */ c("div", { style: { marginTop: 16, height: 80 }, children: [
569
- a === "guess" && !R && /* @__PURE__ */ c("div", { children: [
590
+ )
591
+ },
592
+ z
593
+ );
594
+ })
595
+ }
596
+ );
597
+ })(),
598
+ /* @__PURE__ */ m("div", { style: { marginTop: 16, height: 80 }, children: [
599
+ d === "guess" && !R && /* @__PURE__ */ m("div", { children: [
570
600
  /* @__PURE__ */ t(
571
601
  "input",
572
602
  {
573
603
  type: "text",
574
604
  placeholder: "Type the missing word",
575
- value: H,
576
- onChange: (e) => Y(e.target.value),
605
+ value: W,
606
+ onChange: (e) => K(e.target.value),
577
607
  style: n.gmInput
578
608
  }
579
609
  ),
@@ -581,13 +611,13 @@ function Me() {
581
611
  "button",
582
612
  {
583
613
  style: { ...n.gmButton, marginLeft: 8 },
584
- onClick: se,
614
+ onClick: me,
585
615
  disabled: D,
586
616
  children: D ? "..." : "Check"
587
617
  }
588
618
  )
589
619
  ] }),
590
- R && /* @__PURE__ */ t("button", { type: "button", style: n.gmButton, onClick: le, children: "Next round" })
620
+ R && /* @__PURE__ */ t("button", { type: "button", style: n.gmButton, onClick: ue, children: "Next round" })
591
621
  ] })
592
622
  ] })
593
623
  ] })
@@ -599,6 +629,6 @@ function Me() {
599
629
  );
600
630
  }
601
631
  export {
602
- Me as Game,
603
- ee as themes
632
+ Be as Game,
633
+ re as themes
604
634
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mblinkov/whats-missing",
3
- "version": "20.0.21",
3
+ "version": "20.0.22",
4
4
  "main": "dist/whats-missing.umd.js",
5
5
  "module": "dist/whats-missing.es.js",
6
6
  "types": "dist/index.d.ts",
package/src/Game.tsx CHANGED
@@ -13,7 +13,7 @@ type RoundResult = {
13
13
  };
14
14
 
15
15
 
16
- // ✅ базовый reset
16
+ // ✅ базовый reset (ограничен корнем игры)
17
17
  const globalReset = () => {
18
18
  const style = document.createElement("style");
19
19
  style.textContent = `
@@ -46,7 +46,6 @@ export function useNeutralizeBodyZoom(
46
46
  opts: { minDesktopWidth?: number; forceResize?: boolean } = { minDesktopWidth: 1200, forceResize: false }
47
47
  ) {
48
48
  const saved = useRef<Record<string, string | null> | null>(null);
49
- // извлекаем значение, чтобы использовать в deps
50
49
  const minDesktopWidth = opts.minDesktopWidth ?? 1200;
51
50
  const forceResize = !!opts.forceResize;
52
51
 
@@ -57,26 +56,30 @@ export function useNeutralizeBodyZoom(
57
56
  const getBodyScale = (): number => {
58
57
  try {
59
58
  const body = document.body;
60
- const cs = getComputedStyle(body) as CSSStyleDeclaration & {
61
- zoom?: string;
62
- webkitTransform?: string;
63
- mozTransform?: string;
64
- };
59
+ const cs = getComputedStyle(body) as CSSStyleDeclaration & { zoom?: string };
65
60
 
61
+ // 1) existing inline/computed zoom if present
66
62
  const zoomInline = parseFloat((body.style && body.style.zoom) || "");
67
63
  if (zoomInline && !isNaN(zoomInline) && zoomInline > 0) return zoomInline;
68
-
69
64
  const zoomComputed = parseFloat((cs.zoom as string) || "");
70
65
  if (zoomComputed && !isNaN(zoomComputed) && zoomComputed > 0) return zoomComputed;
71
66
 
72
- const tf = cs.transform || cs.webkitTransform || cs.mozTransform;
73
- if (tf && tf !== "none") {
74
- const m = tf.match(/matrix\(([^)]+)\)/);
75
- if (m) {
76
- const parts = m[1].split(",").map((p) => parseFloat(p));
77
- const a = parts[0];
78
- if (a && !isNaN(a)) return a;
79
- }
67
+ // 2) robust probe: insert a 100px element and measure its bounding rect.
68
+ // This detects any effective visual scale applied to the page (transform on ancestors, UA scaling, etc).
69
+ const probe = document.createElement("div");
70
+ probe.style.position = "absolute";
71
+ probe.style.left = "0";
72
+ probe.style.top = "0";
73
+ probe.style.width = "100px";
74
+ probe.style.height = "1px";
75
+ probe.style.visibility = "hidden";
76
+ probe.style.pointerEvents = "none";
77
+ document.documentElement.appendChild(probe);
78
+ const rect = probe.getBoundingClientRect();
79
+ document.documentElement.removeChild(probe);
80
+ if (rect && rect.width > 0) {
81
+ const measured = rect.width / 100;
82
+ if (!isNaN(measured) && measured > 0) return measured;
80
83
  }
81
84
  } catch (e) {
82
85
  // ignore
@@ -109,17 +112,18 @@ export function useNeutralizeBodyZoom(
109
112
 
110
113
  const inv = 1 / scale;
111
114
 
112
- // safer default: не трогаем width/height, только визуально инвертируем внутри.
113
- // если нужен "flow" компенсации, включать forceResize: true
115
+ // safer default: только визуальная компенсация (не трогаем layout), опция forceResize включит width/height
114
116
  if (forceResize) {
115
117
  el.style.width = `${scale * 100}%`;
116
118
  el.style.height = `${scale * 100}%`;
117
119
  }
120
+
121
+ // масштабируем относительно центра, чтобы не сдвигать центрированный контент
122
+ el.style.transformOrigin = "50% 50%";
118
123
  el.style.transform = `scale(${inv})`;
119
- el.style.transformOrigin = "top left";
120
124
  el.style.willChange = "transform";
121
125
 
122
- if (typeof process !== "undefined" && process.env && process.env.NODE_ENV === "development") {
126
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
123
127
  // eslint-disable-next-line no-console
124
128
  console.debug("[useNeutralizeBodyZoom] applied scale:", { scale, inv, forceResize });
125
129
  }
@@ -139,7 +143,7 @@ export function useNeutralizeBodyZoom(
139
143
  if (s.willChange === null) el.style.removeProperty("will-change");
140
144
  else el.style.willChange = s.willChange as string;
141
145
  saved.current = null;
142
- if (process.env.NODE_ENV === "development") {
146
+ if (typeof process !== "undefined" && process.env?.NODE_ENV === "development") {
143
147
  // eslint-disable-next-line no-console
144
148
  console.debug("[useNeutralizeBodyZoom] restored original styles");
145
149
  }
@@ -176,9 +180,12 @@ export function useNeutralizeBodyZoom(
176
180
 
177
181
 
178
182
  export default function Game() {
179
- const containerRef = useRef<HTMLDivElement | null>(null);
180
- useNeutralizeBodyZoom(containerRef, { minDesktopWidth: 1200, forceResize: false });
181
-
183
+ const containerRef = useRef<HTMLDivElement | null>(null);
184
+ // ref на видимый квадрат (тот, что width/height выставлены в px или %)
185
+ const squareRef = useRef<HTMLDivElement | null>(null);
186
+ const [squareSize, setSquareSize] = useState<number | null>(null);
187
+ useNeutralizeBodyZoom(containerRef, { minDesktopWidth: 1200, forceResize: false });
188
+
182
189
 
183
190
  useEffect(() => {
184
191
  globalReset();
@@ -207,39 +214,59 @@ export default function Game() {
207
214
  const [resultsTable, setResultsTable] = useState<RoundResult[]>([]);
208
215
  const [usedHidden, setUsedHidden] = useState<string[]>([]);
209
216
  const [isMobile, setIsMobile] = useState(false);
210
- const [scale, setScale] = useState(1);
211
217
  const [containerSize, setContainerSize] = useState<number | null>(null);
212
218
 
219
+ // наблюдаем за реальным render-size квадрата — это даёт надежную базу для расчета карточек
220
+ useLayoutEffect(() => {
221
+ const el = squareRef.current;
222
+ if (!el || typeof window === "undefined") return;
223
+ let ro: ResizeObserver | null = null;
224
+ try {
225
+ ro = new ResizeObserver((entries) => {
226
+ for (const e of entries) {
227
+ const cr = e.contentRect;
228
+ setSquareSize(Math.round(Math.min(cr.width, cr.height)));
229
+ }
230
+ });
231
+ ro.observe(el);
232
+ // init
233
+ setSquareSize(Math.round(Math.min(el.clientWidth, el.clientHeight)));
234
+ } catch (err) {
235
+ // ResizeObserver may not be available — fallback to initial measurement
236
+ setSquareSize(Math.round(Math.min(el.clientWidth, el.clientHeight)));
237
+ }
238
+ return () => {
239
+ if (ro) ro.disconnect();
240
+ };
241
+ }, [isMobile]);
242
+
213
243
 
214
244
  // ✅ адаптив под мобилки, планшеты и десктоп
215
245
  useEffect(() => {
216
246
  const resize = () => {
217
- // Prefer visualViewport when available — it's more accurate under page zoom
218
247
  const vv = (window as any).visualViewport;
219
248
  const vw = vv ? Math.round(vv.width) : window.innerWidth;
220
249
  const vh = vv ? Math.round(vv.height) : window.innerHeight;
221
250
 
222
- const mobile = vw < 768;
223
- // tablet flag kept for possible future use
224
- const tablet = vw >= 768 && vw < 1200;
225
-
226
- // treat only phones as "mobile" so tablets use the desktop (square/centered) layout
251
+ // treat narrow screens as mobile; portrait tablets also considered mobile
252
+ const mobile = vw < 768 || (vw >= 768 && vw < 1200 && vh > vw);
227
253
  setIsMobile(mobile);
228
254
 
229
255
  if (mobile) {
230
- // mobile: fluid full-width layout
231
256
  setContainerSize(null);
232
- setScale(1);
233
- } else {
234
- // tablets and desktops: fit into a centered square (max 1000)
235
- const safeFactor = 0.97;
236
- const maxSquare = 1000;
237
- const available = Math.min(vw, vh);
238
- const sizePx = Math.max(360, Math.round(available * safeFactor)); // don't go too small
239
- const finalSize = Math.min(sizePx, maxSquare);
240
- setContainerSize(finalSize);
241
- setScale(1);
257
+ return;
242
258
  }
259
+
260
+ // tablets & desktop: conservative square
261
+ const safeFactor = 0.92; // leave margins for browser UI
262
+ const maxSquare = 1000;
263
+ const gutter = 40;
264
+ const availableW = Math.max(360, vw - gutter);
265
+ const availableH = Math.max(360, vh - gutter);
266
+ const available = Math.min(availableW, availableH);
267
+ const sizePx = Math.max(360, Math.round(available * safeFactor));
268
+ const finalSize = Math.min(sizePx, maxSquare);
269
+ setContainerSize(finalSize);
243
270
  };
244
271
 
245
272
  // run once
@@ -397,34 +424,41 @@ export default function Game() {
397
424
  <div
398
425
  ref={containerRef}
399
426
  style={{
400
- width: "100%",
401
- height: "100vh",
427
+ // center relative to viewport so site layout doesn't push the game off-center
428
+ position: "fixed",
429
+ inset: 0,
402
430
  display: "flex",
403
431
  justifyContent: "center",
404
432
  alignItems: "center",
405
433
  background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
406
434
  transition: "background 0.3s ease",
407
- overflowX: "clip"
435
+ overflow: "hidden",
436
+ zIndex: 9999,
437
+ pointerEvents: "auto",
408
438
  }}
409
439
  >
410
440
  <div
441
+ // visible square that should fit on tablets
442
+ ref={squareRef}
411
443
  style={{
412
- width: isMobile ? "100%" : containerSize || 1000,
413
- height: isMobile ? "100%" : containerSize || 1000,
444
+ width: isMobile ? "100%" : containerSize ? `${containerSize}px` : "1000px",
445
+ height: isMobile ? "100%" : containerSize ? `${containerSize}px` : "1000px",
446
+ maxWidth: "calc(100vw - 24px)",
447
+ maxHeight: "calc(100vh - 24px)",
414
448
  display: "flex",
415
449
  justifyContent: "center",
416
450
  alignItems: "center",
417
451
  overflow: "hidden",
418
452
  borderRadius: isMobile ? 0 : "20px",
419
453
  background: "linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%)",
420
- boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.1)",
421
- margin: isMobile ? "0 auto" : "unset",
422
- position: "relative", // needed so absolute logo is inside the square
454
+ boxShadow: isMobile ? "none" : "0 0 40px rgba(0,0,0,0.08)",
455
+ position: "relative",
423
456
  }}
424
457
  >
425
458
  <div
426
459
  style={{
427
460
  transform: "none",
461
+ transformOrigin: "50% 50%",
428
462
  width: "100%",
429
463
  height: "100%",
430
464
  display: "flex",
@@ -543,50 +577,58 @@ export default function Game() {
543
577
  <p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
544
578
  )}
545
579
  </div>
546
- {phase !== "ready" && (
547
- <div
548
- style={{
549
- ...styles.gmGrid,
550
- gridTemplateColumns: isMobile
551
- ? "repeat(2, 1fr)"
552
- : "repeat(3, 210px)",
553
- gridAutoRows: isMobile ? "150px" : "210px",
554
- gap: isMobile ? "12px" : "20px",
555
- justifyItems: "center",
556
- }}
557
- >
558
- {files.map((file, i) => {
559
- const isHidden =
560
- hiddenIndex === i && phase === "guess" && !answered;
561
- return (
562
- <div
563
- key={i}
564
- style={{
565
- ...styles.gmCard,
566
- ...(result === "correct" && hiddenIndex === i
567
- ? styles.gmCorrect
568
- : {}),
569
- ...(result === "wrong" && hiddenIndex === i
570
- ? styles.gmWrong
571
- : {}),
572
- }}
573
- >
574
- {!isHidden && (
575
- <img
576
- src={file.src}
577
- alt={file.name}
578
- style={{
579
- width: "100%",
580
- height: "100%",
581
- objectFit: "cover",
582
- }}
583
- />
584
- )}
585
- </div>
586
- );
587
- })}
588
- </div>
589
- )}
580
+ {phase !== "ready" && (() => {
581
+ // compute card size from real measured square size (fallback to containerSize)
582
+ const gap = isMobile ? 12 : 20;
583
+ const innerGutters = 32; // logo / paddings estimation
584
+ const effective = squareSize ?? containerSize ?? (isMobile ? 360 : 1000);
585
+ const columns = isMobile ? 2 : 3;
586
+ const cardSize = Math.max(
587
+ 90,
588
+ Math.floor((effective - innerGutters - gap * (columns - 1)) / columns)
589
+ );
590
+
591
+ return (
592
+ <div
593
+ style={{
594
+ ...styles.gmGrid,
595
+ gridTemplateColumns: `repeat(${columns}, ${cardSize}px)`,
596
+ gridAutoRows: `${cardSize}px`,
597
+ gap: `${gap}px`,
598
+ justifyContent: "center",
599
+ justifyItems: "center",
600
+ padding: 8,
601
+ boxSizing: "border-box",
602
+ }}
603
+ >
604
+ {files.map((file, i) => {
605
+ const isHidden = hiddenIndex === i && phase === "guess" && !answered;
606
+ return (
607
+ <div
608
+ key={i}
609
+ style={{
610
+ ...styles.gmCard,
611
+ width: `${cardSize}px`,
612
+ height: `${cardSize}px`,
613
+ padding: isMobile ? 8 : 10,
614
+ boxSizing: "border-box",
615
+ ...(result === "correct" && hiddenIndex === i ? styles.gmCorrect : {}),
616
+ ...(result === "wrong" && hiddenIndex === i ? styles.gmWrong : {}),
617
+ }}
618
+ >
619
+ {!isHidden && (
620
+ <img
621
+ src={file.src}
622
+ alt={file.name}
623
+ style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
624
+ />
625
+ )}
626
+ </div>
627
+ );
628
+ })}
629
+ </div>
630
+ );
631
+ })()}
590
632
  <div style={{ marginTop: 16, height: 80 }}>
591
633
  {phase === "guess" && !answered && (
592
634
  <div>