@mblinkov/whats-missing 20.0.5 → 20.0.7

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,36 +1,29 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),n=require("react"),oe=window.origin+"/cloud/speakid/games/whatsmissing/",M=(r,c)=>r.map(([l,o])=>({src:`${oe}${c}/${o.replace(/\s/g,"%20")}.png`,name:o})),ne=[["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"]],re=[["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"]],ie=[["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"]],B={food:M(ne,"food"),animals:M(re,"animals"),toys:M(ie,"toys")},le=`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),n=require("react"),oe=window.origin+"/cloud/speakid/games/whatsmissing/",M=(r,c)=>r.map(([a,o])=>({src:`${oe}${c}/${o.replace(/\s/g,"%20")}.png`,name:o})),ne=[["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"]],re=[["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"]],ie=[["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"]],B={food:M(ne,"food"),animals:M(re,"animals"),toys:M(ie,"toys")},ae=`
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 r=document.createElement("style");r.id="spin-keyframes",r.innerHTML=le,document.head.appendChild(r)}const ae={animation:"spin 1.4s linear infinite"},s={gmCenterScreen:{position:"relative",zIndex:1,minHeight:"100vh",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:"100vh",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"},gmLogoImg:{height:"clamp(28px, 5vw, 40px)",width:"auto",background:"transparent"},gmHourglass:{fontSize:"42px",...ae}},de=window.origin+"/cloud/speakid/games/whatsmissing/logo.png",ce=()=>{const r=document.createElement("style");r.textContent=`
7
- html, body, #root {
8
- margin: 0;
9
- padding: 0;
10
- height: 100%;
11
- }
12
- *, *::before, *::after {
6
+ `;if(typeof document<"u"&&!document.getElementById("spin-keyframes")){const r=document.createElement("style");r.id="spin-keyframes",r.innerHTML=ae,document.head.appendChild(r)}const le={animation:"spin 1.4s linear infinite"},s={gmCenterScreen:{position:"relative",zIndex:1,minHeight:"100vh",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:"100vh",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",...le}},de=()=>{const r=document.createElement("style");r.textContent=`
7
+ #whats-missing-root, #whats-missing-root * {
13
8
  box-sizing: border-box;
14
- }
15
- body {
16
9
  font-family: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
17
- background-color: #fff;
18
- color: #1f2937;
19
10
  }
20
- button, input {
21
- font-family: inherit;
22
- outline: none;
23
- border: none;
24
- background: none;
11
+
12
+ #whats-missing-root {
13
+ color: #1f2937;
14
+ text-align: center;
15
+ background: linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%);
25
16
  }
26
- img {
17
+
18
+ #whats-missing-root img {
27
19
  max-width: 100%;
28
20
  height: auto;
29
21
  display: block;
30
22
  user-select: none;
31
23
  }
24
+
32
25
  @keyframes spin {
33
26
  from { transform: rotate(0deg); }
34
27
  to { transform: rotate(360deg); }
35
28
  }
36
- `,document.head.appendChild(r)},me=(r,c)=>{const l=Array.from({length:r.length+1},()=>Array(c.length+1).fill(0));for(let o=0;o<=r.length;o++)l[o][0]=o;for(let o=0;o<=c.length;o++)l[0][o]=o;for(let o=1;o<=r.length;o++)for(let a=1;a<=c.length;a++)l[o][a]=r[o-1]===c[a-1]?l[o-1][a-1]:Math.min(l[o-1][a-1],l[o][a-1],l[o-1][a])+1;return l[r.length][c.length]};function ge(){n.useEffect(()=>{ce()},[]);const[r,c]=n.useState(null),[l,o]=n.useState([]),[a,$]=n.useState(4),[h,L]=n.useState(!1),[f,W]=n.useState(1),[u,K]=n.useState(null),[m,y]=n.useState("ready"),[E,G]=n.useState(3),[k,C]=n.useState(7),[T,F]=n.useState(20),[V,v]=n.useState(0),[b,z]=n.useState(!1),[w,D]=n.useState(""),[p,R]=n.useState(!1),[I,q]=n.useState(!1),[P,x]=n.useState(null),[Z,A]=n.useState([]),[J,U]=n.useState([]),[j,Q]=n.useState(!1);n.useEffect(()=>{const t=()=>Q(window.innerWidth<768);return t(),window.addEventListener("resize",t),()=>window.removeEventListener("resize",t)},[]);const Y=t=>[...t].sort(()=>Math.random()-.5).slice(0,6),N=()=>{if(!r)return;const t=Y(B[r]);o(t),L(!0),z(!1),W(1),v(0),A([]),U([]),O(t,[],!0)},O=(t=l,i=J,g=!1)=>{const d=Y(t.length?t:B[r]);let S=Math.floor(Math.random()*d.length),H=d[S].name,_=0;for(;i.includes(H)&&_<20;)S=Math.floor(Math.random()*d.length),H=d[S].name,_++;o(d),K(S),U([...i,H]),R(!1),D(""),F(20),x(null),g?(y("ready"),G(3),C(7)):(y("memorize"),C(7))};n.useEffect(()=>{if(!(!h||b||p)){if(m==="ready")if(E<=0)y("memorize");else{const t=setTimeout(()=>G(i=>i-1),1e3);return()=>clearTimeout(t)}if(m==="memorize")if(k<=0)y("guess");else{const t=setTimeout(()=>C(i=>i-1),1e3);return()=>clearTimeout(t)}if(m==="guess"){if(T<=0){R(!0),x("wrong");const i=l[u].name;A(g=>[...g,{round:f,answer:w,correct:i,result:"wrong"}]);return}const t=setTimeout(()=>F(i=>i-1),1e3);return()=>clearTimeout(t)}}},[m,E,k,T,h,b,p,l,u,f,w]);const X=()=>{if(u===null||I)return;const t=l[u].name,i=w.toLowerCase().trim();let g="wrong";i===t?(v(d=>d+1),x("correct"),g="correct"):me(i,t)===1?(v(d=>d+.5),x("almost"),g="almost"):(x("wrong"),g="wrong"),A(d=>[...d,{round:f,answer:i,correct:t,result:g}]),q(!0),setTimeout(()=>{q(!1),R(!0)},600)},ee=()=>f<a?(W(t=>t+1),O()):z(!0),te=()=>{L(!1),z(!1),c(null)},se=n.useMemo(()=>e.jsx("div",{style:s.gmLogoFixed,children:e.jsx("img",{src:de,alt:"SPEAKID Logo",style:s.gmLogoImg})}),[]);return e.jsx("div",{id:"whats-missing-root",style:{all:"initial",display:"block"},children:e.jsxs("div",{style:{all:"unset"},children:[!j&&se,!r&&!h&&e.jsxs("div",{style:s.gmCenterScreen,children:[e.jsx("h1",{style:s.gmHeadline1,children:"WHAT'S MISSING?"}),e.jsx("p",{style:s.gmBodyM,children:"Select a theme:"}),e.jsx("div",{style:{display:"flex",gap:16},children:["animals","food","toys"].map(t=>e.jsx("button",{style:s.gmButton,onClick:()=>c(t),children:t==="animals"?"🐶 Animals":t==="food"?"🍎 Food":"🧸 Toys"},t))}),e.jsxs("div",{style:{marginTop:24},children:[e.jsx("p",{style:s.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:{...s.gmButton,...a===t?s.gmButtonActive:{}},onClick:()=>$(t),children:t},t))})]})]}),r&&!h&&e.jsxs("div",{style:s.gmCenterScreen,children:[e.jsxs("h1",{style:s.gmHeadline1,children:["Theme selected: ",r]}),e.jsxs("p",{style:s.gmBodyM,children:["Rounds: ",a]}),e.jsx("button",{style:s.gmButton,onClick:N,children:"▶ Start game"})]}),b&&e.jsxs("div",{style:s.gmCenterScreen,children:[e.jsx("h1",{style:s.gmHeadline1,children:"Results"}),e.jsxs("h2",{style:s.gmHeadline3,children:["Your score: ",V," / ",a]}),e.jsxs("table",{style:s.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:Z.map((t,i)=>e.jsxs("tr",{children:[e.jsx("td",{style:s.gmTableCell,children:t.round}),e.jsx("td",{style:s.gmTableCell,children:t.answer||"—"}),e.jsx("td",{style:s.gmTableCell,children:t.correct}),e.jsx("td",{style:s.gmTableCell,children:t.result==="correct"?"✔ Correct":t.result==="almost"?"◐ Almost (0.5)":"✘ Wrong"})]},i))})]}),e.jsxs("div",{style:{display:"flex",gap:12,marginTop:24},children:[e.jsx("button",{style:s.gmButton,onClick:N,children:"🔁 Play again"}),e.jsx("button",{style:s.gmButton,onClick:te,children:"⬅️ Choose theme"})]})]}),h&&!b&&e.jsxs("div",{style:s.gmGameLayout,children:[e.jsxs("div",{style:{minHeight:160,display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:[m==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("h1",{style:{...s.gmHeadline1,color:"#ec4c44"},children:"GET READY"}),e.jsx("div",{style:s.gmHourglass,children:"⏳"})]}),m==="memorize"&&e.jsxs("p",{style:{...s.gmBodyM,color:"#10b981"},children:["MEMORIZE (",k,")"]}),m==="guess"&&!p&&e.jsxs("p",{style:s.gmBodyM,children:["⏳ Time left: ",T,"s"]})]}),m!=="ready"&&e.jsx("div",{style:{...s.gmGrid,gridTemplateColumns:j?"repeat(2, 1fr)":"repeat(3, 210px)",gridAutoRows:j?"150px":"210px",gap:j?"12px":"20px",justifyItems:"center",...window.innerWidth>2e3?{gridTemplateColumns:"repeat(3, 260px)",gridAutoRows:"260px",gap:"28px"}:{},...window.innerWidth>2560?{gridTemplateColumns:"repeat(3, 300px)",gridAutoRows:"300px",gap:"32px"}:{}},children:l.map((t,i)=>{const g=u===i&&m==="guess"&&!p;return e.jsx("div",{style:{...s.gmCard,...P==="correct"&&u===i?s.gmCorrect:{},...P==="wrong"&&u===i?s.gmWrong:{}},children:!g&&e.jsx("img",{src:t.src,alt:t.name,style:{width:"100%",height:"100%",objectFit:"cover"}})},i)})}),e.jsxs("div",{style:{marginTop:16,height:80},children:[m==="guess"&&!p&&e.jsxs("div",{children:[e.jsx("input",{type:"text",placeholder:"Type the missing word",value:w,onChange:t=>D(t.target.value),style:s.gmInput}),e.jsx("button",{style:{...s.gmButton,marginLeft:8},onClick:X,disabled:I,children:I?"...":"Check"})]}),p&&e.jsx("button",{style:s.gmButton,onClick:ee,children:"Next round"})]})]})]})})}exports.Game=ge;exports.themes=B;
29
+ `,document.head.appendChild(r)},ce=(r,c)=>{const a=Array.from({length:r.length+1},()=>Array(c.length+1).fill(0));for(let o=0;o<=r.length;o++)a[o][0]=o;for(let o=0;o<=c.length;o++)a[0][o]=o;for(let o=1;o<=r.length;o++)for(let l=1;l<=c.length;l++)a[o][l]=r[o-1]===c[l-1]?a[o-1][l-1]:Math.min(a[o-1][l-1],a[o][l-1],a[o-1][l])+1;return a[r.length][c.length]};function me(){n.useEffect(()=>{de()},[]);const[r,c]=n.useState(null),[a,o]=n.useState([]),[l,_]=n.useState(4),[h,L]=n.useState(!1),[f,W]=n.useState(1),[u,$]=n.useState(null),[m,y]=n.useState("ready"),[E,G]=n.useState(3),[k,C]=n.useState(7),[T,F]=n.useState(20),[K,v]=n.useState(0),[b,z]=n.useState(!1),[w,D]=n.useState(""),[p,R]=n.useState(!1),[I,q]=n.useState(!1),[P,x]=n.useState(null),[Z,A]=n.useState([]),[J,U]=n.useState([]),[j,Q]=n.useState(!1);n.useEffect(()=>{const t=()=>Q(window.innerWidth<768);return t(),window.addEventListener("resize",t),()=>window.removeEventListener("resize",t)},[]);const Y=t=>[...t].sort(()=>Math.random()-.5).slice(0,6),N=()=>{if(!r)return;const t=Y(B[r]);o(t),L(!0),z(!1),W(1),v(0),A([]),U([]),O(t,[],!0)},O=(t=a,i=J,g=!1)=>{const d=Y(t.length?t:B[r]);let S=Math.floor(Math.random()*d.length),H=d[S].name,V=0;for(;i.includes(H)&&V<20;)S=Math.floor(Math.random()*d.length),H=d[S].name,V++;o(d),$(S),U([...i,H]),R(!1),D(""),F(20),x(null),g?(y("ready"),G(3),C(7)):(y("memorize"),C(7))};n.useEffect(()=>{if(!(!h||b||p)){if(m==="ready")if(E<=0)y("memorize");else{const t=setTimeout(()=>G(i=>i-1),1e3);return()=>clearTimeout(t)}if(m==="memorize")if(k<=0)y("guess");else{const t=setTimeout(()=>C(i=>i-1),1e3);return()=>clearTimeout(t)}if(m==="guess"){if(T<=0){R(!0),x("wrong");const i=a[u].name;A(g=>[...g,{round:f,answer:w,correct:i,result:"wrong"}]);return}const t=setTimeout(()=>F(i=>i-1),1e3);return()=>clearTimeout(t)}}},[m,E,k,T,h,b,p,a,u,f,w]);const X=()=>{if(u===null||I)return;const t=a[u].name,i=w.toLowerCase().trim();let g="wrong";i===t?(v(d=>d+1),x("correct"),g="correct"):ce(i,t)===1?(v(d=>d+.5),x("almost"),g="almost"):(x("wrong"),g="wrong"),A(d=>[...d,{round:f,answer:i,correct:t,result:g}]),q(!0),setTimeout(()=>{q(!1),R(!0)},600)},ee=()=>f<l?(W(t=>t+1),O()):z(!0),te=()=>{L(!1),z(!1),c(null)},se=n.useMemo(()=>e.jsx("div",{style:s.gmLogoFixed,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:s.gmLogoImg,loading:"lazy"})]})}),[]);return e.jsxs("div",{id:"whats-missing-root",children:[!j&&se,!r&&!h&&e.jsxs("div",{style:s.gmCenterScreen,children:[e.jsx("h1",{style:s.gmHeadline1,children:"WHAT'S MISSING?"}),e.jsx("p",{style:s.gmBodyM,children:"Select a theme:"}),e.jsx("div",{style:{display:"flex",gap:16},children:["animals","food","toys"].map(t=>e.jsx("button",{style:s.gmButton,onClick:()=>c(t),children:t==="animals"?"🐶 Animals":t==="food"?"🍎 Food":"🧸 Toys"},t))}),e.jsxs("div",{style:{marginTop:24},children:[e.jsx("p",{style:s.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:{...s.gmButton,...l===t?s.gmButtonActive:{}},onClick:()=>_(t),children:t},t))})]})]}),r&&!h&&e.jsxs("div",{style:s.gmCenterScreen,children:[e.jsxs("h1",{style:s.gmHeadline1,children:["Theme selected: ",r]}),e.jsxs("p",{style:s.gmBodyM,children:["Rounds: ",l]}),e.jsx("button",{style:s.gmButton,onClick:N,children:"▶ Start game"})]}),b&&e.jsxs("div",{style:s.gmCenterScreen,children:[e.jsx("h1",{style:s.gmHeadline1,children:"Results"}),e.jsxs("h2",{style:s.gmHeadline3,children:["Your score: ",K," / ",l]}),e.jsxs("table",{style:s.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:Z.map((t,i)=>e.jsxs("tr",{children:[e.jsx("td",{style:s.gmTableCell,children:t.round}),e.jsx("td",{style:s.gmTableCell,children:t.answer||"—"}),e.jsx("td",{style:s.gmTableCell,children:t.correct}),e.jsx("td",{style:s.gmTableCell,children:t.result==="correct"?"✔ Correct":t.result==="almost"?"◐ Almost (0.5)":"✘ Wrong"})]},i))})]}),e.jsxs("div",{style:{display:"flex",gap:12,marginTop:24},children:[e.jsx("button",{style:s.gmButton,onClick:N,children:"🔁 Play again"}),e.jsx("button",{style:s.gmButton,onClick:te,children:"⬅️ Choose theme"})]})]}),h&&!b&&e.jsxs("div",{style:s.gmGameLayout,children:[e.jsxs("div",{style:{minHeight:160,display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center"},children:[m==="ready"&&e.jsxs(e.Fragment,{children:[e.jsx("h1",{style:{...s.gmHeadline1,color:"#ec4c44"},children:"GET READY"}),e.jsx("div",{style:s.gmHourglass,children:"⏳"})]}),m==="memorize"&&e.jsxs("p",{style:{...s.gmBodyM,color:"#10b981"},children:["MEMORIZE (",k,")"]}),m==="guess"&&!p&&e.jsxs("p",{style:s.gmBodyM,children:["⏳ Time left: ",T,"s"]})]}),m!=="ready"&&e.jsx("div",{style:{...s.gmGrid,gridTemplateColumns:j?"repeat(2, 1fr)":"repeat(3, 210px)",gridAutoRows:j?"150px":"210px",gap:j?"12px":"20px",justifyItems:"center",...window.innerWidth>2e3?{gridTemplateColumns:"repeat(3, 260px)",gridAutoRows:"260px",gap:"28px"}:{},...window.innerWidth>2560?{gridTemplateColumns:"repeat(3, 300px)",gridAutoRows:"300px",gap:"32px"}:{}},children:a.map((t,i)=>{const g=u===i&&m==="guess"&&!p;return e.jsx("div",{style:{...s.gmCard,...P==="correct"&&u===i?s.gmCorrect:{},...P==="wrong"&&u===i?s.gmWrong:{}},children:!g&&e.jsx("img",{src:t.src,alt:t.name,style:{width:"100%",height:"100%",objectFit:"cover"}})},i)})}),e.jsxs("div",{style:{marginTop:16,height:80},children:[m==="guess"&&!p&&e.jsxs("div",{children:[e.jsx("input",{type:"text",placeholder:"Type the missing word",value:w,onChange:t=>D(t.target.value),style:s.gmInput}),e.jsx("button",{style:{...s.gmButton,marginLeft:8},onClick:X,disabled:I,children:I?"...":"Check"})]}),p&&e.jsx("button",{style:s.gmButton,onClick:ee,children:"Next round"})]})]})]})}exports.Game=me;exports.themes=B;
@@ -69,7 +69,7 @@ const le = window.origin + "/cloud/speakid/games/whatsmissing/", W = (r, m) => r
69
69
  ["teddy_bear", "teddy bear"],
70
70
  ["yo-yo", "yo-yo"],
71
71
  ["crayons", "crayons"]
72
- ], O = {
72
+ ], K = {
73
73
  food: W(ae, "food"),
74
74
  animals: W(de, "animals"),
75
75
  toys: W(ce, "toys")
@@ -209,7 +209,7 @@ const ge = {
209
209
  overflow: "hidden",
210
210
  textOverflow: "ellipsis"
211
211
  },
212
- // ✅ Исправленный логотип: без белого фона, не ломает верстку
212
+ // ✅ Обновлено только для качества логотипа
213
213
  gmLogoFixed: {
214
214
  position: "absolute",
215
215
  top: "16px",
@@ -217,51 +217,51 @@ const ge = {
217
217
  width: "120px",
218
218
  zIndex: 10,
219
219
  pointerEvents: "none",
220
- background: "transparent"
220
+ background: "transparent",
221
+ transform: "none",
222
+ willChange: "auto"
221
223
  },
222
224
  gmLogoImg: {
223
225
  height: "clamp(28px, 5vw, 40px)",
224
226
  width: "auto",
225
- background: "transparent"
227
+ background: "transparent",
228
+ objectFit: "contain",
229
+ imageRendering: "auto",
230
+ transform: "translateZ(0)",
231
+ backfaceVisibility: "hidden",
232
+ WebkitFontSmoothing: "antialiased"
226
233
  },
227
234
  gmHourglass: {
228
235
  fontSize: "42px",
229
236
  ...ge
230
237
  }
231
- }, pe = window.origin + "/cloud/speakid/games/whatsmissing/logo.png", ue = () => {
238
+ }, pe = () => {
232
239
  const r = document.createElement("style");
233
240
  r.textContent = `
234
- html, body, #root {
235
- margin: 0;
236
- padding: 0;
237
- height: 100%;
238
- }
239
- *, *::before, *::after {
241
+ #whats-missing-root, #whats-missing-root * {
240
242
  box-sizing: border-box;
241
- }
242
- body {
243
243
  font-family: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
244
- background-color: #fff;
245
- color: #1f2937;
246
244
  }
247
- button, input {
248
- font-family: inherit;
249
- outline: none;
250
- border: none;
251
- background: none;
245
+
246
+ #whats-missing-root {
247
+ color: #1f2937;
248
+ text-align: center;
249
+ background: linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%);
252
250
  }
253
- img {
251
+
252
+ #whats-missing-root img {
254
253
  max-width: 100%;
255
254
  height: auto;
256
255
  display: block;
257
256
  user-select: none;
258
257
  }
258
+
259
259
  @keyframes spin {
260
260
  from { transform: rotate(0deg); }
261
261
  to { transform: rotate(360deg); }
262
262
  }
263
263
  `, document.head.appendChild(r);
264
- }, he = (r, m) => {
264
+ }, ue = (r, m) => {
265
265
  const a = Array.from({ length: r.length + 1 }, () => Array(m.length + 1).fill(0));
266
266
  for (let n = 0; n <= r.length; n++) a[n][0] = n;
267
267
  for (let n = 0; n <= m.length; n++) a[0][n] = n;
@@ -270,24 +270,24 @@ const ge = {
270
270
  a[n][d] = r[n - 1] === m[d - 1] ? a[n - 1][d - 1] : Math.min(a[n - 1][d - 1], a[n][d - 1], a[n - 1][d]) + 1;
271
271
  return a[r.length][m.length];
272
272
  };
273
- function xe() {
273
+ function ye() {
274
274
  L(() => {
275
- ue();
275
+ pe();
276
276
  }, []);
277
- const [r, m] = l(null), [a, n] = l([]), [d, V] = l(4), [f, j] = l(!1), [x, E] = l(1), [u, Z] = l(null), [g, b] = l("ready"), [G, F] = l(3), [v, S] = l(7), [z, D] = l(20), [J, R] = l(0), [w, I] = l(!1), [k, U] = l(""), [h, A] = l(!1), [H, Y] = l(!1), [N, y] = l(null), [Q, M] = l([]), [X, P] = l([]), [C, ee] = l(!1);
277
+ const [r, m] = l(null), [a, n] = l([]), [d, O] = l(4), [f, j] = l(!1), [x, E] = l(1), [u, Z] = l(null), [g, b] = l("ready"), [G, F] = l(3), [v, S] = l(7), [z, D] = l(20), [J, R] = l(0), [w, I] = l(!1), [k, U] = l(""), [h, A] = l(!1), [H, Y] = l(!1), [N, y] = l(null), [Q, M] = l([]), [X, P] = l([]), [C, ee] = l(!1);
278
278
  L(() => {
279
279
  const e = () => ee(window.innerWidth < 768);
280
280
  return e(), window.addEventListener("resize", e), () => window.removeEventListener("resize", e);
281
281
  }, []);
282
- const _ = (e) => [...e].sort(() => Math.random() - 0.5).slice(0, 6), $ = () => {
282
+ const V = (e) => [...e].sort(() => Math.random() - 0.5).slice(0, 6), _ = () => {
283
283
  if (!r) return;
284
- const e = _(O[r]);
285
- n(e), j(!0), I(!1), E(1), R(0), M([]), P([]), q(e, [], !0);
286
- }, q = (e = a, i = X, p = !1) => {
287
- const c = _(e.length ? e : O[r]);
288
- let T = Math.floor(Math.random() * c.length), B = c[T].name, K = 0;
289
- for (; i.includes(B) && K < 20; )
290
- T = Math.floor(Math.random() * c.length), B = c[T].name, K++;
284
+ const e = V(K[r]);
285
+ n(e), j(!0), I(!1), E(1), R(0), M([]), P([]), $(e, [], !0);
286
+ }, $ = (e = a, i = X, p = !1) => {
287
+ const c = V(e.length ? e : K[r]);
288
+ let T = Math.floor(Math.random() * c.length), B = c[T].name, q = 0;
289
+ for (; i.includes(B) && q < 20; )
290
+ T = Math.floor(Math.random() * c.length), B = c[T].name, q++;
291
291
  n(c), Z(T), P([...i, B]), A(!1), U(""), D(20), y(null), p ? (b("ready"), F(3), S(7)) : (b("memorize"), S(7));
292
292
  };
293
293
  L(() => {
@@ -337,32 +337,41 @@ function xe() {
337
337
  if (u === null || H) return;
338
338
  const e = a[u].name, i = k.toLowerCase().trim();
339
339
  let p = "wrong";
340
- i === e ? (R((c) => c + 1), y("correct"), p = "correct") : he(i, e) === 1 ? (R((c) => c + 0.5), y("almost"), p = "almost") : (y("wrong"), p = "wrong"), M((c) => [
340
+ i === e ? (R((c) => c + 1), y("correct"), p = "correct") : ue(i, e) === 1 ? (R((c) => c + 0.5), y("almost"), p = "almost") : (y("wrong"), p = "wrong"), M((c) => [
341
341
  ...c,
342
342
  { round: x, answer: i, correct: e, result: p }
343
343
  ]), Y(!0), setTimeout(() => {
344
344
  Y(!1), A(!0);
345
345
  }, 600);
346
- }, oe = () => x < d ? (E((e) => e + 1), q()) : I(!0), ne = () => {
346
+ }, oe = () => x < d ? (E((e) => e + 1), $()) : I(!0), ne = () => {
347
347
  j(!1), I(!1), m(null);
348
348
  }, re = se(
349
- () => /* @__PURE__ */ o("div", { style: t.gmLogoFixed, children: /* @__PURE__ */ o("img", { src: pe, alt: "SPEAKID Logo", style: t.gmLogoImg }) }),
349
+ () => /* @__PURE__ */ o("div", { style: t.gmLogoFixed, children: /* @__PURE__ */ s("picture", { children: [
350
+ /* @__PURE__ */ o(
351
+ "source",
352
+ {
353
+ srcSet: window.origin + "/cloud/speakid/games/whatsmissing/logo.svg",
354
+ type: "image/svg+xml"
355
+ }
356
+ ),
357
+ /* @__PURE__ */ o(
358
+ "img",
359
+ {
360
+ src: window.origin + "/cloud/speakid/games/whatsmissing/logo.png",
361
+ alt: "SPEAKID Logo",
362
+ style: t.gmLogoImg,
363
+ loading: "lazy"
364
+ }
365
+ )
366
+ ] }) }),
350
367
  []
351
368
  );
352
- return /* @__PURE__ */ o("div", { id: "whats-missing-root", style: { all: "initial", display: "block" }, children: /* @__PURE__ */ s("div", { style: { all: "unset" }, children: [
369
+ return /* @__PURE__ */ s("div", { id: "whats-missing-root", children: [
353
370
  !C && re,
354
371
  !r && !f && /* @__PURE__ */ s("div", { style: t.gmCenterScreen, children: [
355
372
  /* @__PURE__ */ o("h1", { style: t.gmHeadline1, children: "WHAT'S MISSING?" }),
356
373
  /* @__PURE__ */ o("p", { style: t.gmBodyM, children: "Select a theme:" }),
357
- /* @__PURE__ */ o("div", { style: { display: "flex", gap: 16 }, children: ["animals", "food", "toys"].map((e) => /* @__PURE__ */ o(
358
- "button",
359
- {
360
- style: t.gmButton,
361
- onClick: () => m(e),
362
- children: e === "animals" ? "🐶 Animals" : e === "food" ? "🍎 Food" : "🧸 Toys"
363
- },
364
- e
365
- )) }),
374
+ /* @__PURE__ */ o("div", { style: { display: "flex", gap: 16 }, children: ["animals", "food", "toys"].map((e) => /* @__PURE__ */ o("button", { style: t.gmButton, onClick: () => m(e), children: e === "animals" ? "🐶 Animals" : e === "food" ? "🍎 Food" : "🧸 Toys" }, e)) }),
366
375
  /* @__PURE__ */ s("div", { style: { marginTop: 24 }, children: [
367
376
  /* @__PURE__ */ o("p", { style: t.gmBodyS, children: "Choose number of rounds:" }),
368
377
  /* @__PURE__ */ o("div", { style: { display: "flex", gap: 12, marginTop: 8 }, children: [3, 4, 5].map((e) => /* @__PURE__ */ o(
@@ -372,7 +381,7 @@ function xe() {
372
381
  ...t.gmButton,
373
382
  ...d === e ? t.gmButtonActive : {}
374
383
  },
375
- onClick: () => V(e),
384
+ onClick: () => O(e),
376
385
  children: e
377
386
  },
378
387
  e
@@ -388,7 +397,7 @@ function xe() {
388
397
  "Rounds: ",
389
398
  d
390
399
  ] }),
391
- /* @__PURE__ */ o("button", { style: t.gmButton, onClick: $, children: "▶ Start game" })
400
+ /* @__PURE__ */ o("button", { style: t.gmButton, onClick: _, children: "▶ Start game" })
392
401
  ] }),
393
402
  w && /* @__PURE__ */ s("div", { style: t.gmCenterScreen, children: [
394
403
  /* @__PURE__ */ o("h1", { style: t.gmHeadline1, children: "Results" }),
@@ -413,7 +422,7 @@ function xe() {
413
422
  ] }, i)) })
414
423
  ] }),
415
424
  /* @__PURE__ */ s("div", { style: { display: "flex", gap: 12, marginTop: 24 }, children: [
416
- /* @__PURE__ */ o("button", { style: t.gmButton, onClick: $, children: "🔁 Play again" }),
425
+ /* @__PURE__ */ o("button", { style: t.gmButton, onClick: _, children: "🔁 Play again" }),
417
426
  /* @__PURE__ */ o("button", { style: t.gmButton, onClick: ne, children: "⬅️ Choose theme" })
418
427
  ] })
419
428
  ] }),
@@ -519,9 +528,9 @@ function xe() {
519
528
  h && /* @__PURE__ */ o("button", { style: t.gmButton, onClick: oe, children: "Next round" })
520
529
  ] })
521
530
  ] })
522
- ] }) });
531
+ ] });
523
532
  }
524
533
  export {
525
- xe as Game,
526
- O as themes
534
+ ye as Game,
535
+ K as themes
527
536
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mblinkov/whats-missing",
3
- "version": "20.0.5",
3
+ "version": "20.0.7",
4
4
  "main": "dist/whats-missing.umd.js",
5
5
  "module": "dist/whats-missing.es.js",
6
6
  "types": "dist/index.d.ts",
@@ -174,7 +174,7 @@ export const styles: Record<string, CSSProperties> = {
174
174
  textOverflow: "ellipsis",
175
175
  },
176
176
 
177
- // ✅ Исправленный логотип: без белого фона, не ломает верстку
177
+ // ✅ Обновлено только для качества логотипа
178
178
  gmLogoFixed: {
179
179
  position: "absolute",
180
180
  top: "16px",
@@ -183,12 +183,19 @@ export const styles: Record<string, CSSProperties> = {
183
183
  zIndex: 10,
184
184
  pointerEvents: "none",
185
185
  background: "transparent",
186
+ transform: "none",
187
+ willChange: "auto",
186
188
  },
187
189
 
188
190
  gmLogoImg: {
189
191
  height: "clamp(28px, 5vw, 40px)",
190
192
  width: "auto",
191
193
  background: "transparent",
194
+ objectFit: "contain",
195
+ imageRendering: "auto",
196
+ transform: "translateZ(0)",
197
+ backfaceVisibility: "hidden",
198
+ WebkitFontSmoothing: "antialiased",
192
199
  },
193
200
 
194
201
  gmReadyWrapper: {
package/src/Game.tsx CHANGED
@@ -3,8 +3,6 @@ import { themes } from "./themes";
3
3
  import { styles } from "./Game.styles";
4
4
  import type { Theme } from "./themes";
5
5
 
6
- const logo = window.origin + "/cloud/speakid/games/whatsmissing/logo.png";
7
-
8
6
  type ImageItem = { src: string; name: string };
9
7
  type RoundResult = {
10
8
  round: number;
@@ -13,34 +11,28 @@ type RoundResult = {
13
11
  result: "correct" | "almost" | "wrong";
14
12
  };
15
13
 
14
+ // ✅ только изоляция, всё остальное без изменений
16
15
  const globalReset = () => {
17
16
  const style = document.createElement("style");
18
17
  style.textContent = `
19
- html, body, #root {
20
- margin: 0;
21
- padding: 0;
22
- height: 100%;
23
- }
24
- *, *::before, *::after {
18
+ #whats-missing-root, #whats-missing-root * {
25
19
  box-sizing: border-box;
26
- }
27
- body {
28
20
  font-family: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
29
- background-color: #fff;
30
- color: #1f2937;
31
21
  }
32
- button, input {
33
- font-family: inherit;
34
- outline: none;
35
- border: none;
36
- background: none;
22
+
23
+ #whats-missing-root {
24
+ color: #1f2937;
25
+ text-align: center;
26
+ background: linear-gradient(to bottom, #fff8f8 0%, #f9fafb 100%);
37
27
  }
38
- img {
28
+
29
+ #whats-missing-root img {
39
30
  max-width: 100%;
40
31
  height: auto;
41
32
  display: block;
42
33
  user-select: none;
43
34
  }
35
+
44
36
  @keyframes spin {
45
37
  from { transform: rotate(0deg); }
46
38
  to { transform: rotate(360deg); }
@@ -224,220 +216,226 @@ export default function Game() {
224
216
  setTheme(null);
225
217
  };
226
218
 
219
+ // ✅ логотип с fallback SVG → PNG
227
220
  const MemoizedLogo = useMemo(
228
221
  () => (
229
222
  <div style={styles.gmLogoFixed}>
230
- <img src={logo} alt="SPEAKID Logo" style={styles.gmLogoImg} />
223
+ <picture>
224
+ <source
225
+ srcSet={window.origin + "/cloud/speakid/games/whatsmissing/logo.svg"}
226
+ type="image/svg+xml"
227
+ />
228
+ <img
229
+ src={window.origin + "/cloud/speakid/games/whatsmissing/logo.png"}
230
+ alt="SPEAKID Logo"
231
+ style={styles.gmLogoImg}
232
+ loading="lazy"
233
+ />
234
+ </picture>
231
235
  </div>
232
236
  ),
233
237
  []
234
238
  );
235
239
 
236
240
  return (
237
- <div id="whats-missing-root" style={{ all: "initial", display: "block" }}>
238
- <div style={{ all: "unset" }}>
239
- {!isMobile && MemoizedLogo}
241
+ <div id="whats-missing-root">
242
+ {!isMobile && MemoizedLogo}
240
243
 
241
- {!theme && !started && (
242
- <div style={styles.gmCenterScreen}>
243
- <h1 style={styles.gmHeadline1}>WHAT'S MISSING?</h1>
244
- <p style={styles.gmBodyM}>Select a theme:</p>
245
- <div style={{ display: "flex", gap: 16 }}>
246
- {["animals", "food", "toys"].map((t) => (
244
+ {!theme && !started && (
245
+ <div style={styles.gmCenterScreen}>
246
+ <h1 style={styles.gmHeadline1}>WHAT'S MISSING?</h1>
247
+ <p style={styles.gmBodyM}>Select a theme:</p>
248
+ <div style={{ display: "flex", gap: 16 }}>
249
+ {["animals", "food", "toys"].map((t) => (
250
+ <button key={t} style={styles.gmButton} onClick={() => setTheme(t as Theme)}>
251
+ {t === "animals" ? "🐶 Animals" : t === "food" ? "🍎 Food" : "🧸 Toys"}
252
+ </button>
253
+ ))}
254
+ </div>
255
+ <div style={{ marginTop: 24 }}>
256
+ <p style={styles.gmBodyS}>Choose number of rounds:</p>
257
+ <div style={{ display: "flex", gap: 12, marginTop: 8 }}>
258
+ {[3, 4, 5].map((n) => (
247
259
  <button
248
- key={t}
249
- style={styles.gmButton}
250
- onClick={() => setTheme(t as Theme)}
260
+ key={n}
261
+ style={{
262
+ ...styles.gmButton,
263
+ ...(rounds === n ? styles.gmButtonActive : {}),
264
+ }}
265
+ onClick={() => setRounds(n)}
251
266
  >
252
- {t === "animals" ? "🐶 Animals" : t === "food" ? "🍎 Food" : "🧸 Toys"}
267
+ {n}
253
268
  </button>
254
269
  ))}
255
270
  </div>
256
- <div style={{ marginTop: 24 }}>
257
- <p style={styles.gmBodyS}>Choose number of rounds:</p>
258
- <div style={{ display: "flex", gap: 12, marginTop: 8 }}>
259
- {[3, 4, 5].map((n) => (
260
- <button
261
- key={n}
262
- style={{
263
- ...styles.gmButton,
264
- ...(rounds === n ? styles.gmButtonActive : {}),
265
- }}
266
- onClick={() => setRounds(n)}
267
- >
268
- {n}
269
- </button>
270
- ))}
271
- </div>
272
- </div>
273
271
  </div>
274
- )}
272
+ </div>
273
+ )}
275
274
 
276
- {theme && !started && (
277
- <div style={styles.gmCenterScreen}>
278
- <h1 style={styles.gmHeadline1}>Theme selected: {theme}</h1>
279
- <p style={styles.gmBodyM}>Rounds: {rounds}</p>
275
+ {theme && !started && (
276
+ <div style={styles.gmCenterScreen}>
277
+ <h1 style={styles.gmHeadline1}>Theme selected: {theme}</h1>
278
+ <p style={styles.gmBodyM}>Rounds: {rounds}</p>
279
+ <button style={styles.gmButton} onClick={startGame}>
280
+ ▶ Start game
281
+ </button>
282
+ </div>
283
+ )}
284
+
285
+ {finished && (
286
+ <div style={styles.gmCenterScreen}>
287
+ <h1 style={styles.gmHeadline1}>Results</h1>
288
+ <h2 style={styles.gmHeadline3}>
289
+ Your score: {score} / {rounds}
290
+ </h2>
291
+ <table style={styles.gmTable}>
292
+ <thead>
293
+ <tr>
294
+ <th>Round</th>
295
+ <th>Your Answer</th>
296
+ <th>Correct</th>
297
+ <th>Result</th>
298
+ </tr>
299
+ </thead>
300
+ <tbody>
301
+ {resultsTable.map((r, i) => (
302
+ <tr key={i}>
303
+ <td style={styles.gmTableCell}>{r.round}</td>
304
+ <td style={styles.gmTableCell}>{r.answer || "—"}</td>
305
+ <td style={styles.gmTableCell}>{r.correct}</td>
306
+ <td style={styles.gmTableCell}>
307
+ {r.result === "correct"
308
+ ? "✔ Correct"
309
+ : r.result === "almost"
310
+ ? "◐ Almost (0.5)"
311
+ : "✘ Wrong"}
312
+ </td>
313
+ </tr>
314
+ ))}
315
+ </tbody>
316
+ </table>
317
+ <div style={{ display: "flex", gap: 12, marginTop: 24 }}>
280
318
  <button style={styles.gmButton} onClick={startGame}>
281
- Start game
319
+ 🔁 Play again
320
+ </button>
321
+ <button style={styles.gmButton} onClick={exitGame}>
322
+ ⬅️ Choose theme
282
323
  </button>
283
324
  </div>
284
- )}
325
+ </div>
326
+ )}
285
327
 
286
- {finished && (
287
- <div style={styles.gmCenterScreen}>
288
- <h1 style={styles.gmHeadline1}>Results</h1>
289
- <h2 style={styles.gmHeadline3}>
290
- Your score: {score} / {rounds}
291
- </h2>
292
- <table style={styles.gmTable}>
293
- <thead>
294
- <tr>
295
- <th>Round</th>
296
- <th>Your Answer</th>
297
- <th>Correct</th>
298
- <th>Result</th>
299
- </tr>
300
- </thead>
301
- <tbody>
302
- {resultsTable.map((r, i) => (
303
- <tr key={i}>
304
- <td style={styles.gmTableCell}>{r.round}</td>
305
- <td style={styles.gmTableCell}>{r.answer || "—"}</td>
306
- <td style={styles.gmTableCell}>{r.correct}</td>
307
- <td style={styles.gmTableCell}>
308
- {r.result === "correct"
309
- ? "✔ Correct"
310
- : r.result === "almost"
311
- ? "◐ Almost (0.5)"
312
- : "✘ Wrong"}
313
- </td>
314
- </tr>
315
- ))}
316
- </tbody>
317
- </table>
318
- <div style={{ display: "flex", gap: 12, marginTop: 24 }}>
319
- <button style={styles.gmButton} onClick={startGame}>
320
- 🔁 Play again
321
- </button>
322
- <button style={styles.gmButton} onClick={exitGame}>
323
- ⬅️ Choose theme
324
- </button>
325
- </div>
328
+ {started && !finished && (
329
+ <div style={styles.gmGameLayout}>
330
+ <div
331
+ style={{
332
+ minHeight: 160,
333
+ display: "flex",
334
+ flexDirection: "column",
335
+ justifyContent: "center",
336
+ alignItems: "center",
337
+ }}
338
+ >
339
+ {phase === "ready" && (
340
+ <>
341
+ <h1 style={{ ...styles.gmHeadline1, color: "#ec4c44" }}>GET READY</h1>
342
+ <div style={styles.gmHourglass}>⏳</div>
343
+ </>
344
+ )}
345
+ {phase === "memorize" && (
346
+ <p style={{ ...styles.gmBodyM, color: "#10b981" }}>
347
+ MEMORIZE ({memorizeTime})
348
+ </p>
349
+ )}
350
+ {phase === "guess" && !answered && (
351
+ <p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
352
+ )}
326
353
  </div>
327
- )}
328
354
 
329
- {started && !finished && (
330
- <div style={styles.gmGameLayout}>
355
+ {phase !== "ready" && (
331
356
  <div
332
357
  style={{
333
- minHeight: 160,
334
- display: "flex",
335
- flexDirection: "column",
336
- justifyContent: "center",
337
- alignItems: "center",
358
+ ...styles.gmGrid,
359
+ gridTemplateColumns: isMobile ? "repeat(2, 1fr)" : "repeat(3, 210px)",
360
+ gridAutoRows: isMobile ? "150px" : "210px",
361
+ gap: isMobile ? "12px" : "20px",
362
+ justifyItems: "center",
363
+ ...(window.innerWidth > 2000
364
+ ? {
365
+ gridTemplateColumns: "repeat(3, 260px)",
366
+ gridAutoRows: "260px",
367
+ gap: "28px",
368
+ }
369
+ : {}),
370
+ ...(window.innerWidth > 2560
371
+ ? {
372
+ gridTemplateColumns: "repeat(3, 300px)",
373
+ gridAutoRows: "300px",
374
+ gap: "32px",
375
+ }
376
+ : {}),
338
377
  }}
339
378
  >
340
- {phase === "ready" && (
341
- <>
342
- <h1 style={{ ...styles.gmHeadline1, color: "#ec4c44" }}>GET READY</h1>
343
- <div style={styles.gmHourglass}>⏳</div>
344
- </>
345
- )}
346
- {phase === "memorize" && (
347
- <p style={{ ...styles.gmBodyM, color: "#10b981" }}>
348
- MEMORIZE ({memorizeTime})
349
- </p>
350
- )}
351
- {phase === "guess" && !answered && (
352
- <p style={styles.gmBodyM}>⏳ Time left: {timeLeft}s</p>
353
- )}
379
+ {files.map((file, i) => {
380
+ const isHidden =
381
+ hiddenIndex === i && phase === "guess" && !answered;
382
+ return (
383
+ <div
384
+ key={i}
385
+ style={{
386
+ ...styles.gmCard,
387
+ ...(result === "correct" && hiddenIndex === i
388
+ ? styles.gmCorrect
389
+ : {}),
390
+ ...(result === "wrong" && hiddenIndex === i
391
+ ? styles.gmWrong
392
+ : {}),
393
+ }}
394
+ >
395
+ {!isHidden && (
396
+ <img
397
+ src={file.src}
398
+ alt={file.name}
399
+ style={{
400
+ width: "100%",
401
+ height: "100%",
402
+ objectFit: "cover",
403
+ }}
404
+ />
405
+ )}
406
+ </div>
407
+ );
408
+ })}
354
409
  </div>
410
+ )}
355
411
 
356
- {phase !== "ready" && (
357
- <div
358
- style={{
359
- ...styles.gmGrid,
360
- gridTemplateColumns: isMobile ? "repeat(2, 1fr)" : "repeat(3, 210px)",
361
- gridAutoRows: isMobile ? "150px" : "210px",
362
- gap: isMobile ? "12px" : "20px",
363
- justifyItems: "center",
364
- ...(window.innerWidth > 2000
365
- ? {
366
- gridTemplateColumns: "repeat(3, 260px)",
367
- gridAutoRows: "260px",
368
- gap: "28px",
369
- }
370
- : {}),
371
- ...(window.innerWidth > 2560
372
- ? {
373
- gridTemplateColumns: "repeat(3, 300px)",
374
- gridAutoRows: "300px",
375
- gap: "32px",
376
- }
377
- : {}),
378
- }}
379
- >
380
- {files.map((file, i) => {
381
- const isHidden =
382
- hiddenIndex === i && phase === "guess" && !answered;
383
- return (
384
- <div
385
- key={i}
386
- style={{
387
- ...styles.gmCard,
388
- ...(result === "correct" && hiddenIndex === i
389
- ? styles.gmCorrect
390
- : {}),
391
- ...(result === "wrong" && hiddenIndex === i
392
- ? styles.gmWrong
393
- : {}),
394
- }}
395
- >
396
- {!isHidden && (
397
- <img
398
- src={file.src}
399
- alt={file.name}
400
- style={{
401
- width: "100%",
402
- height: "100%",
403
- objectFit: "cover",
404
- }}
405
- />
406
- )}
407
- </div>
408
- );
409
- })}
412
+ <div style={{ marginTop: 16, height: 80 }}>
413
+ {phase === "guess" && !answered && (
414
+ <div>
415
+ <input
416
+ type="text"
417
+ placeholder="Type the missing word"
418
+ value={inputValue}
419
+ onChange={(e) => setInputValue(e.target.value)}
420
+ style={styles.gmInput}
421
+ />
422
+ <button
423
+ style={{ ...styles.gmButton, marginLeft: 8 }}
424
+ onClick={checkAnswer}
425
+ disabled={animating}
426
+ >
427
+ {animating ? "..." : "Check"}
428
+ </button>
410
429
  </div>
411
430
  )}
412
-
413
- <div style={{ marginTop: 16, height: 80 }}>
414
- {phase === "guess" && !answered && (
415
- <div>
416
- <input
417
- type="text"
418
- placeholder="Type the missing word"
419
- value={inputValue}
420
- onChange={(e) => setInputValue(e.target.value)}
421
- style={styles.gmInput}
422
- />
423
- <button
424
- style={{ ...styles.gmButton, marginLeft: 8 }}
425
- onClick={checkAnswer}
426
- disabled={animating}
427
- >
428
- {animating ? "..." : "Check"}
429
- </button>
430
- </div>
431
- )}
432
- {answered && (
433
- <button style={styles.gmButton} onClick={nextRound}>
434
- Next round
435
- </button>
436
- )}
437
- </div>
431
+ {answered && (
432
+ <button style={styles.gmButton} onClick={nextRound}>
433
+ Next round
434
+ </button>
435
+ )}
438
436
  </div>
439
- )}
440
- </div>
437
+ </div>
438
+ )}
441
439
  </div>
442
440
  );
443
441
  }