1const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-BNkiwcKy.js","assets/index-DMmTf-Pi.js","assets/react-vendor-Dw2UxApT.js","assets/index-BRzZVyqk.js","assets/ui-icons-B7iubBRc.js","assets/jszip.min-CK9NPRCS.js","assets/_commonjs-dynamic-modules-TDtrdbi3.js","assets/EBML.min-BfozmedC.js"])))=>i.map(i=>d[i]); 2import{r as m,j as t,a as th,c as DC}from"./react-vendor-Dw2UxApT.js";import{C as nt,F as LC,X as Tt,R as it,L as ky,U as oh,T as Ho,D as Gt,H as nw,A as VC,B as zC,M as qd,a as In,b as Hd,P as _e,S as iw,c as bt,d as sw,e as OC,f as cw,g as wc,h as Ga,i as UC,j as wy,E as lw,k as FC,l as BC,I as Ld,m as Sy,n as WC,o as $C,V as dw,p as GC,q as xy,r as qC,s as HC,G as Ry,t as Py,u as uw,v as JC,w as gm,K as Vd,x as pw,y as dd,z as Ua,J as Go,N as ud,O as pd,Q as fm,W as bm,Y as rh,Z as Cy,_ as _C,$ as YC,a0 as KC,a1 as ZC,a2 as ah,a3 as XC,a4 as QC}from"./ui-icons-B7iubBRc.js";(function(){const o=document.createElement("link").relList;if(o&&o.supports&&o.supports("modulepreload"))return;for(const c of document.querySelectorAll('link[rel="modulepreload"]'))i(c);new MutationObserver(c=>{for(const l of c)if(l.type==="childList")for(const p of l.addedNodes)p.tagName==="LINK"&&p.rel==="modulepreload"&&i(p)}).observe(document,{childList:!0,subtree:!0});function r(c){const l={};return c.integrity&&(l.integrity=c.integrity),c.referrerPolicy&&(l.referrerPolicy=c.referrerPolicy),c.crossOrigin==="use-credentials"?l.credentials="include":c.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function i(c){if(c.ep)return;c.ep=!0;const l=r(c);fetch(c.href,l)}})();const mw="voxalike:translation-recovery",My=["button","input","select","option","textarea","[role='button']","[role='menuitem']","[role='status']","[role='tab']","[aria-live]","[data-translation-stable]"].join(",");let ym=!1;function eM(e){jy(e);const o=new MutationObserver(r=>{for(const i of r)for(const c of i.addedNodes)c instanceof HTMLElement&&jy(c)});return o.observe(e,{childList:!0,subtree:!0}),()=>o.disconnect()}function tM(e){return hw()?(nh(e),!0):!1}function Lh(e){if(!oM(e)||ym||hw())return!1;ym=!0;try{window.sessionStorage.setItem(mw,"active")}catch{return ym=!1,!1}return window.setTimeout(()=>window.location.reload(),0),!0}function oM(e){const o=e instanceof Error?e.message:String(e??"");return o.includes("removeChild")||o.includes("insertBefore")||o.includes("not a child of this node")||o.includes("The child can not be found in the parent")}function hw(){try{return window.sessionStorage.getItem(mw)==="active"}catch{return!1}}function jy(e){e.matches(My)&&nh(e);for(const o of e.querySelectorAll(My))nh(o)}function nh(e){e.setAttribute("translate","no"),e.classList.add("notranslate")}function $t(e){if(!e||e.mode!=="coop"||e.completedAt)return;const o=e.assignments[e.currentAssignmentIndex];if(o?.playerId)return JSON.stringify([e.startedAt,e.currentAssignmentIndex,o.clipId,o.playerId,e.lastRedoAt??""])}function md(e){const o=e?.takePreview;return e?.status==="playing"&&o?.audioDataUrl&&o.turnKey===$t(e.game)?o:void 0}function vm(e){const o=e?.takePreviewAvailable;return e?.status==="playing"&&o?.turnKey===$t(e.game)?o:void 0}class rM{constructor(o){this.io=o}io;current;active;stage(o,r){return this.current?.key!==o&&(this.current={key:o,value:r,id:crypto.randomUUID(),state:"waiting",sent:!1,requested:!1,committing:!1},this.io.changed(r,"waiting")),this.refresh(),this.current.id}announcement(){const o=this.current;return o&&!o.committing?{id:o.id,value:o.value}:void 0}request(o){const r=this.current;!r||r.id!==o||r.committing||(r.requested=!0,this.refresh())}refresh(){const o=this.current;if(!o||!o.requested||this.active||o.committing||o.state!=="waiting"||!this.io.canShare(o.value))return;o.state="uploading",this.io.changed(o.value,o.state);const r=Promise.resolve().then(async()=>{if(this.current!==o||o.committing||!this.io.canShare(o.value))return;const i=await this.io.encode(o.value);this.current!==o||o.committing||!this.io.canShare(o.value)||(o.sent=!0,o.preview=await this.io.upload(o.value,o.id,i),o.state="ready")}
2).catch(()=>{o.state="failed"}).finally(()=>{o.state==="uploading"&&(o.state="waiting"),this.active===r&&(this.active=void 0),this.current===o&&!o.committing&&this.io.changed(o.value,o.state,o.preview),this.current!==o&&this.refresh()});o.work=r,this.active=r}retry(){const o=this.current;!o||o.committing||o.state!=="failed"||(o.state="waiting",this.refresh())}async forSubmission(o){const r=this.current;return!r||r.key!==o?{}:(r.committing=!0,await r.work,{previewId:r.sent?r.id:void 0,preview:r.preview})}releaseSubmission(){this.current&&(this.current.committing=!1)}cancel(){this.current=void 0}}const aM="R-A-19765954-3",nM="R-A-19765954-2",Ty="https://yandex.ru/ads/system/context.js";let Ey;function iM(){return Ey??=fetch("/api/ads/config",{credentials:"same-origin"}).then(async e=>e.ok&&(await e.json()).provider==="yandex").catch(()=>!1),Ey}function sM({authLoading:e,adFree:o}){const[r,i]=m.useState(!1),[c,l]=m.useState(!1),p=!1;return m.useEffect(()=>{if(e||o||p)return;const f=window;let b=!1,y,w,M=0;const E=()=>{b||i(!1)},z=()=>{if(!b&&(y=f.Ya?.Context?.AdvManager,!!y))try{const N=y.getPlatform()==="desktop"?"desktop":"touch";w=N==="desktop"?aM:nM,i(!0),M=requestAnimationFrame(()=>{if(!(b||!y||!w))try{y.render({blockId:w,type:"floorAd",platform:N,onClose:E,onError:E})}catch{E()}})}catch{E()}};return iM().then(N=>{if(!(b||!N)){if(f.Ya?.Context?.AdvManager){z();return}if(f.yaContextCb??=[],f.yaContextCb.push(z),!document.querySelector(`script[src="${Ty}"]`)){const j=document.createElement("script");j.src=Ty,j.async=!0,document.head.append(j)}}}),()=>{b=!0,cancelAnimationFrame(M);const N=f.yaContextCb;if(Array.isArray(N)){const j=N.indexOf(z);j>=0&&N.splice(j,1)}if(y&&w)try{y.destroy({blockId:w})}catch{}}},[e,o,p]),e||o||!r?null:t.jsx("div",{className:"home-floor-ad-space","aria-hidden":!0,children:null})}const Ay="https://hb.vntsm.com/v4/live/vms/sites/voxalike.com/index.js",cM=[{slug:"billboard",config:"billboard",slotId:"slot-1",label:"Billboard",size:"970x250",position:"top"},{slug:"leaderboard",config:"leaderboard",slotId:"slot-2",label:"Leaderboard",size:"728x90",position:"top"},{slug:"double-mpu",config:"double_mpu",slotId:"slot-3",label:"Double MPU",size:"300x600",position:"rail"},{slug:"mpu",config:"mpu",slotId:"slot-4",label:"MPU",size:"300x250",position:"rail"},{slug:"skyscraper",config:"skyscraper",slotId:"slot-7",label:"Skyscraper",size:"160x600",position:"rail"},{slug:"mobile-banner",config:"mobile_banner",slotId:"slot-5",label:"Mobile banner",size:"320x50",position:"top"},{slug:"mobile-mpu",config:"mobile_mpu",slotId:"slot-6",label:"Mobile MPU",size:"300x250",position:"inline"},{slug:"video",config:"video",slotId:"slot-8",label:"Video",size:"640x360",position:"article"},{slug:"desktop-takeover",config:"desktop_takeover",slotId:"slot-9",label:"Desktop takeover",size:"takeover",position:"top"},{slug:"mobile-takeover",config:"mobile_takeover",slotId:"slot-10",label:"Mobile takeover",size:"takeover",position:"top"}];function gw(e,o){if((e.length>1?e.replace(/\/+$/,""):e)!=="/guide/faq")return;const c=(typeof o=="string"?new URLSearchParams(o):o).get("venatus_review");return cM.find(l=>l.slug===c)}let Iy=!1;function lM(){if(document.querySelector(`script[src="${Ay}"]`))return;const e=document.createElement("script");e.src=Ay,e.async=!0,e.dataset.venatusProsper="true",document.head.append(e)}function dM(e){Iy||(Iy=!0,e.__VM??=[],e.__VM.push((o,r)=>{const i=r.Instances?.pageManager;i?.on("navigated",()=>{i.newPageSession(!1)},!1)}))}function hd({placement:e}){const o=m.useRef(null);return m.useEffect(()=>{const r=window,i=o.current;if(!i||i.dataset.venatusRequested==="true")return;let c=!1,l;i.dataset.venatusRequested="true",i.dataset.venatusState="queued";const p=(f,b)=>{if(!(c||!i.isConnected)){i.dataset.venatusState="displaying";try{l=b.Config.get(e.config).display(i),i.dataset.venatusState=l?"registered":"requested"}catch(y){throw i.dataset.venatusState="error",y}}};return r.__VM??=[],dM(r),r.__VM.push(p),lM(),()=>{c=!0;const f=r.__VM;if(Array.isArray(f)){const b=f.indexOf(p);b>=0&&f.splice(b,1)}l&&f?.push(b=>{if(typeof l?.remove=="function"){l.remove();return}const y=l?.instance?.();y!==void 0&&b.removePlacement?.(y)}),delete i.dataset.venatusRequested,delete i.dataset.venatusState}},[e]),t.jsxs("aside",{className:`venatus-guide-review venatus-guide-review-${e.position} venatus-guide-${e.slug}`,"aria-label":"Advertisement","data-venatus-review-placement":e.slug,children:[t.jsx("span",{className:"venatus-guide-review-label",children:"Advertisement"}),t.jsx("div",{ref:o,id:e.slotId,"data-placeholder":`${e.label} · ${e.size}`})]})}const uM=new Set(["multiplayer_socket_authenticated","multiplayer_socket_closed","multiplayer_room_left","multiplayer_connection_interrupted"]);function pM(e){return!uM.has(e)}const mM=8e3,hM=8e3,gM=1e4;async function gd(){const e=await fetch("/api/auth/me",{credentials:"same-origin"});return e.ok?(await e.json()).user??null:null}async function fM(){if(!(await fetch("/auth/logout",{method:"POST",credentials:"same-origin"})).ok)throw new Error("Sign out failed")}async function bM(){const e=await fetch("/api/supporter/codes",{credentials:"same-origin",cache:"no-store"});
2if(!e.ok)throw new Error("Could not load Supporter gift codes");const o=await e.json();return Array.isArray(o.codes)?o.codes:[]}async function yM(e){const o=await fetch("/api/supporter/gift-recipient",{method:"POST",credentials:"same-origin",headers:{"content-type":"application/json"},body:JSON.stringify({email:e})});if(!o.ok)throw new Error(await zn(o));const r=await o.json();if(!r.recipient)throw new Error("No matching Voxalike player was found.");return r.recipient}async function vM(){const e=await fetch("/api/payments/stripe/status",{credentials:"same-origin",cache:"no-store"});if(!e.ok)throw new Error("Could not confirm payment status");const o=await e.json();return Array.isArray(o.orders)?o.orders:[]}async function kM(){return we("/api/session",{},{operation:"Session",timeoutMs:mM})}async function Ny(e){return we("/api/round/start",e,{operation:"Next clip",timeoutMs:hM})}async function wM(e){return we("/api/score",e,{operation:"Score",timeoutMs:gM})}function fd(e,o,r){if(!pM(e))return;const i=JSON.stringify({eventName:e,sessionId:o,payload:r});try{if(typeof navigator<"u"&&navigator.sendBeacon&&navigator.sendBeacon("/api/analytics/game-event",new Blob([i],{type:"application/json"})))return;fetch("/api/analytics/game-event",{method:"POST",headers:{"content-type":"application/json"},body:i,keepalive:!0}).catch(()=>{})}catch{return}}async function SM(e){const o=new Date().toISOString().slice(0,10),r=`voxalike:support-view:${e}`;try{if(localStorage.getItem(r)===o)return}catch{}try{if(!(await fetch("/api/analytics/support-view",{method:"POST",headers:{"content-type":"application/json"},body:"{}",keepalive:!0})).ok)return;try{localStorage.setItem(r,o)}catch{return}}catch{return}}async function Dy(e){return we("/api/rooms/create",e)}async function xM(e={}){const o=new URLSearchParams;e.language&&e.language!=="all"&&o.set("language",e.language),e.status&&e.status!=="all"&&o.set("status",e.status);const r=await fetch(`/api/rooms/public${o.size?`?${o.toString()}`:""}`,{credentials:"same-origin"});if(!r.ok)throw new Error(await zn(r));return(await r.json()).rooms??[]}async function RM(e){return we("/api/rooms/join",e)}async function Ly(e){return we("/api/rooms/state",e,{timeoutMs:7e3})}const PM="https://realtime.voxalike.com";function CM(e,o=window.location.href){const r=new URL(o),i=r.hostname==="voxalike.com"||r.hostname==="www.voxalike.com"?PM:r.origin,c=new URL("/api/rooms/socket",i);return c.protocol=c.protocol==="https:"?"wss:":"ws:",c.searchParams.set("roomCode",e),c.toString()}async function MM(){try{const e=await fetch("/api/version",{cache:"no-store",credentials:"same-origin"});if(!e.ok)return;const o=await e.json(),r=typeof o.version=="string"?o.version.trim():"";return r&&r!=="unknown"?r:void 0}catch{return}}async function jM(e){return we("/api/rooms/start",e)}async function TM(e){return we("/api/rooms/recovery-audio",e,{timeoutMs:2e4})}async function EM(e){return we("/api/rooms/prepare-recovery",e,{timeoutMs:15e3})}async function AM(e){return we("/api/rooms/lobby",e)}async function IM(e){return we("/api/rooms/export-complete",e)}async function NM(e){return we("/api/rooms/ready",e)}async function DM(e){return we("/api/rooms/pack",e)}async function LM(e){return we("/api/rooms/claim-role",e)}async function VM(e){return we("/api/rooms/release-role",e)}async function zM(e){return we("/api/rooms/begin-role-selection",e)}async function OM(e){return we("/api/rooms/end-role-selection",e)}async function UM(e){return we("/api/rooms/limit",e)}async function Vy(e){return we("/api/rooms/leave",e)}async function FM(e){return we("/api/rooms/kick",e)}async function BM(e){return we("/api/rooms/report",e)}async function WM(e){const{audioDataUrl:o,...r}=e;return kw({...r,stage:"preview",onDemand:!0},o)}async function zy(e){if(e.audioDataUrl?.startsWith("data:audio/")){const{audioDataUrl:o,...r}=e;return kw({...r,stage:"final"},o)}return we("/api/rooms/take",e)}async function $M(e){return we("/api/rooms/claim",e)}async function GM(e){return we("/api/rooms/unclaim",e)}async function qM(e){return we("/api/rooms/redo-last",e)}async function HM(e){return we("/api/rooms/skip-line",e)}const JM="v2",_M=2*6e4;function fw(e,o){const r=Date.now(),i=new URLSearchParams({catalog:JM,bucket:String(Math.floor(r/_M))});return o.force&&i.set("refresh",String(r)),`${e}?${i.toString()}`}function bw(e){if(e.force)return{cache:"reload",headers:{"X-Workshop-Refresh":"1"}}}async function Oy(e={}){const o=await fetch(fw("/api/workshop/packs",e),bw(e));if(!o.ok){const i=await o.json().catch(()=>({error:o.statusText}));throw new Error(typeof i.error=="string"?i.error:"Workshop unavailable")}return(await o.json()).packs??[]}async function Uy(e={}){const o=await fetch(fw("/api/workshop/judge-mods",e),bw(e));if(!o.ok){const i=await o.json().catch(()=>({error:o.statusText}));throw new Error(typeof i.error=="string"?i.error:"Judge Mods unavailable")}return(await o.json()).judgeMods??[]}let ec;async function YM(e){const o=await Vh("/api/workshop/library-sync",{credentials:"same-origin",...e.force?{cache:"reload",headers:{"X-Workshop-Refresh":"1"}}:{}});if(!o.ok){const i=await o.json().catch(()=>({error:o.statusText}));throw new Error(typeof i.error=="string"?i.error:"Workshop subscriptions unavailable")}const r=await o.json();return{packs:r.packs??[],judgeMods:r.judgeMods??[]}}function km(e={}){if(ec)return ec;const r=YM(e).finally(()=>{ec===r&&(ec=void 0)});return ec=r,r}async function zd(e){const o=new URLSearchParams({kind:"dub"});e.id&&o.set("id",e.id),e.slug&&o.set("slug",e.slug);const r=await Vh(`/api/workshop/lookup?${o.toString()}`,{credentials:"same-origin"});if(r.status===404)return;if(!r.ok)throw new Error(await zn(r));return(await r.json()).pack}async function KM(e){const o=e.id?(await zd({id:e.id}))?.slug:e.slug;if(!o)throw new Error("This mod is no longer available in Workshop.");const r=new FormData;r.set("reason",e.reason),r.set("details",e.details);const i=await fetch(`/workshop/${encodeURIComponent(o)}/report`,{method:"POST",credentials:"same-origin",headers:{Accept:"application/json"},body:r});if(!i.ok)throw new Error(await zn(i));if(!(await i.json()).reported)throw new Error("Report could not be submitted.")}const yw=220,vw=50,Jd=[];let Nn;function mu(e){return new Promise((o,r)=>{if(Jd.push({...e,resolve:o,reject:r}),Nn!==void 0&&window.clearTimeout(Nn),Jd.length>=vw){ih();return}Nn=window.setTimeout(()=>{ih()},yw)})}async function ih(){Nn!==void 0&&(window.clearTimeout(Nn),Nn=void 0);const e=Jd.splice(0,vw);if(!e.length)return;const o=new Map;for(const r of e)o.set(`${r.kind}:${r.id}`,{kind:r.kind,id:r.id,subscribed:r.subscribed});try{const r=await Vh("/api/workshop/subscriptions-batch",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({changes:Array.from(o.values())}),credentials:"same-origin"});if(!r.ok)throw new Error(await zn(r));e.forEach(({resolve:i})=>i())}catch(r){e.forEach(({reject:i})=>i(r))}finally{Jd.length&&(Nn=window.setTimeout(()=>{ih()},yw))}}async function Fy(e){if(e.packId)return mu({kind:"dub",id:e.packId,subscribed:!0});await we("/api/workshop/subscribe",e)}async function ZM(e){if(e.packId)return mu({kind:"dub",id:e.packId,subscribed:!1});await we("/api/workshop/unsubscribe",e)}async function XM(e){if(e.judgeModId)return mu({kind:"judge",id:e.judgeModId,subscribed:!0});await we("/api/workshop/judge-mod-subscribe",e)}async function QM(e){if(e.judgeModId)return mu({kind:"judge",id:e.judgeModId,subscribed:!1});await we("/api/workshop/judge-mod-unsubscribe",e)}async function ej(e){return we("/api/workshop/rate",e)}const bd=[1e3,3e3];async function Vh(e,o){let r;for(let i=0;i<=bd.length;i+=1){try{const l=await fetch(e,o);if(!tj(l.status)||i===bd.length)return l;await l.body?.cancel().catch(()=>{})}catch(l){if(r=l,i===bd.length)throw l}const c=bd[i];await rj(c+oj())}throw r instanceof Error?r:new Error("Workshop unavailable")}function tj(e){return e===429||e===502||e===503||e===504}function oj(){const e=new Uint32Array(1);return crypto.getRandomValues(e),e[0]%181}function rj(e){return new Promise(o=>globalThis.setTimeout(o,e))}async function we(e,o,r={}){const i=r.timeoutMs?new AbortController:void 0,c=crypto.randomUUID(),l=r.timeoutMs?window.setTimeout(()=>{i?.abort()},r.timeoutMs):void 0;try{const p=await fetch(e,{method:"POST",headers:{"content-type":"application/json","x-voxalike-request-id":c},body:JSON.stringify(o),signal:i?.signal});if(!p.ok)throw new Error(await zn(p));return p.json()}catch(p){if(ww(p)){const f=Math.round((r.timeoutMs??0)/1e3);throw new Error(`${r.operation??"Network"} request timed out after ${f} seconds. Your recording is still saved locally. Try again. Request ID: ${c}`)}throw p}finally{l!==void 0&&window.clearTimeout(l)}}async function kw(e,o){const r=crypto.randomUUID(),i=new AbortController,c=2e4,l=window.setTimeout(()=>i.abort(),c),p=new FormData;p.set("metadata",JSON.stringify(e)),p.set("audio",aj(o),"room-take.wav");try{const f=await fetch("/api/rooms/audio",{method:"POST",headers:{"x-voxalike-request-id":r},body:p,signal:i.signal});if(!f.ok)throw new Error(await zn(f));return f.json()}catch(f){throw ww(f)?new Error(`Room recording upload timed out after ${Math.round(c/1e3)} seconds. Your recording is still saved locally. Try again. Request ID: ${r}`):f}finally{window.clearTimeout(l)}}function aj(e){const o=e.indexOf(","),r=e.slice(0,o);if(o<=0||!r.startsWith("data:audio/")||!r.includes(";
2base64"))throw new Error("Recording audio is invalid");const i=r.slice(5,r.indexOf(";"))||"audio/wav",c=atob(e.slice(o+1)),l=new Uint8Array(c.length);for(let p=0;p<c.length;p+=1)l[p]=c.charCodeAt(p);return new Blob([l],{type:i})}function ww(e){return e instanceof Error&&e.name==="AbortError"}async function zn(e){const o=e.statusText||`Request failed (${e.status})`;if((e.headers.get("content-type")??"").includes("application/json")){const c=await e.json().catch(()=>{});if(c&&typeof c.error=="string"){const l=c.error.trim();if(l)return l}return o}return(await e.text().catch(()=>"")).trim()||o}const yd={fr:{SUPPORTER:"SUPPORTER",WORKSHOP:"WORKSHOP",Mods:"Mods","Sign in":"Se connecter","Single Player":"Solo","Support Voxalike":"Soutenir Voxalike","Voxalike Player":"Joueur Voxalike","Supporter purchase":"Achat Supporter","Give Supporter":"Offrir Supporter","Gift Supporter for US$5":"Offrir Supporter pour 5 $US","Copy all unused":"Copier tous les codes inutilisés","Download unused":"Télécharger les codes inutilisés",Redeemed:"Utilisé","Ready to redeem":"Prêt à être utilisé","New Run":"Nouvelle partie","Create Room":"Créer une salle","All Rooms":"Toutes les salles","Room visibility":"Visibilité de la salle","Private Room":"Salle privée","Room Language":"Langue de la salle","Room Code":"Code de la salle","Join Room":"Rejoindre la salle","Leave Room":"Quitter la salle","Back to room":"Retour à la salle","Start Dubbing":"Commencer le doublage","My Take":"Ma prise","Role Claim":"Sélection libre des rôles","Claim Mode":"Sélection libre","Round Robin":"Tour par tour",Claim:"Choisir",Release:"Libérer","Room Chat":"Chat de la salle","Message the room":"Ãcrire dans le chat","Preparing Stage":"Préparation de la scène","Voxalike News":"Actualités Voxalike"},it:{SUPPORTER:"SUPPORTER",WORKSHOP:"WORKSHOP",Mods:"Mod","Voxalike Account":"Account Voxalike","Account details":"Dettagli dell'account","Single Player":"Giocatore singolo","Support Voxalike":"Sostieni Voxalike","Voxalike Player":"Giocatore Voxalike","Supporter purchase":"Acquisto Supporter","Give Supporter":"Regala Supporter","Gift Supporter for US$5":"Regala Supporter per 5 USD","New Run":"Nuova partita","Create Room":"Crea stanza","All Rooms":"Tutte le stanze","Room visibility":"Visibilità della stanza","Private Room":"Stanza privata","Room Language":"Lingua della stanza","Room Code":"Codice della stanza","Join Room":"Entra nella stanza","Leave Room":"Lascia la stanza","Back to room":"Torna alla stanza","Start Dubbing":"Inizia il doppiaggio","My Take":"La mia registrazione","Role Claim":"Selezione libera dei ruoli","Claim Mode":"Selezione libera","Round Robin":"A turni",Claim:"Scegli",Release:"Libera","Room Chat":"Chat della stanza","Message the room":"Scrivi nella chat","Preparing Stage":"Preparazione della scena","Voxalike News":"Novità di Voxalike"},es:{SUPPORTER:"SUPPORTER",WORKSHOP:"WORKSHOP",Mods:"Mods","Support Voxalike":"Apoyar a Voxalike","Voxalike Player":"Jugador de Voxalike","Supporter purchase":"Compra de Supporter","Give Supporter":"Regalar Supporter","Gift Supporter for US$5":"Regalar Supporter por 5 USD",Record:"Grabar","New Run":"Nueva partida","Create Room":"Crear sala","All Rooms":"Todas las salas","Room visibility":"Visibilidad de la sala","Private Room":"Sala privada","Room Language":"Idioma de la sala","Room Code":"Código de la sala","Join Room":"Unirse a la sala","Leave Room":"Salir de la sala","Back to room":"Volver a la sala","Start Dubbing":"Empezar el doblaje","My Take":"Mi grabación","Role Claim":"Selección libre de roles","Claim Mode":"Selección libre","Round Robin":"Por turnos",Claim:"Elegir",Release:"Liberar","Room Chat":"Chat de la sala","Message the room":"Escribir en el chat","Preparing Stage":"Preparando la escena","Voxalike News":"Noticias de Voxalike"},pt:{SUPPORTER:"SUPPORTER",WORKSHOP:"WORKSHOP",Mods:"Mods","Sign in":"Entrar","Single Player":"Um jogador","Support Voxalike":"Apoiar o Voxalike","Voxalike Player":"Jogador do Voxalike","Supporter purchase":"Compra de Supporter","Give Supporter":"Oferecer Supporter","Gift Supporter for US$5":"Oferecer Supporter por 5 USD","New Run":"Nova partida","Create Room":"Criar sala","All Rooms":"Todas as salas","Room visibility":"Visibilidade da sala","Private Room":"Sala privada","Room Language":"Idioma da sala","Room Code":"Código da sala","Join Room":"Entrar na sala","Leave Room":"Sair da sala","Back to room":"Voltar à sala","Start Dubbing":"C
2omeçar a dobragem","My Take":"A minha gravação",Next:"Avançar","Role Claim":"Escolha livre de funções","Claim Mode":"Escolha livre","Round Robin":"Por turnos",Claim:"Escolher",Release:"Libertar","Room Chat":"Chat da sala","Message the room":"Escrever no chat","Preparing Stage":"Preparação da cena","Voxalike News":"NotÃcias do Voxalike"}},vd={fr:{"Interface Language":"Langue de l'interface","Choose the language used by the game interface.":"Choisissez la langue utilisée par l'interface du jeu.",Language:"Langue",PLAY:"JOUER",NEWS:"NOUVELLES","DEVELOPER NEWS":"NOUVELLES DES DÃVELOPPEURS","Voxalike News":"Voxalike Actualités",GUIDE:"GUIDE",WORKSHOP:"ATELIER",SUPPORTER:"SUPPORTEUR",SUPPORT:"SOUTIEN","Sign in":"Connectez-vous","Account menu":"Menu du compte",Notifications:"Notifications",Profile:"Profil","Account details":"Détails du compte","Edit Profile":"Modifier le profil",Studio:"Atelier","Support Again":"Soutenir à nouveau","Become a Supporter":"Devenez un Supporter","Support Voxalike":"Prise en charge de Voxalike","Voxalike Player":"Voxalike Joueur","Supporter profile preview":"Aperçu du profil Supporter","Profile preview":"Aperçu du profil","6 name colors":"6 couleurs de nom","3 avatar frames":"3 cadres d'avatar","4 avatar frames":"4 cadres d'avatar","3 avatar frames + 1 animated bonus frame":"3 cadres d'avatar + 1 cadre animé bonus","Try on {frame}":"Essayer {frame}","Supporter purchase":"Supporter achat","one time":"une fois","No subscription":"Pas d'abonnement","Animated Supporter badge":"Badge animé Supporter","Checkout could not start. Please try again.":"Le paiement n'a pas pu démarrer. Veuillez réessayer.","Checkout was canceled. Nothing was charged.":"Le paiement a été annulé. Rien n'a été facturé.","Payment is being confirmed. Your benefits will appear here automatically.":"Le paiement est en cours de confirmation. Vos avantages apparaîtront ici automatiquement.","Buy Supporter for US$5":"Achetez Supporter pour 5 USD","Gift code checkout is not available yet.":"Le paiement par code cadeau n'est pas encore disponible.","Payment is being confirmed. Your gift code will appear here automatically.":"Le paiement est en cours de confirmation. Votre code cadeau apparaîtra ici automatiquement.","Buy Supporter or a gift code":"Achetez Supporter ou un code cadeau","Activate on this account":"Activer sur ce compte","Supporter benefits start after payment.":"Les avantages Supporter commencent après le paiement.","Buy a Supporter gift code":"Achetez un code cadeau Supporter","Give Supporter":"Donnez Supporter","Manage gift codes":"Gérer les codes cadeaux","SUPPORTER GIFT":"CADEAU DE SUPPORTEUR","Gift delivery method":"Mode de livraison du cadeau","Send to a player":"Envoyer à un joueur","Delivered directly to their account":"Livré directement sur leur compte","Buy gift codes":"Acheter des codes cadeaux","Share the codes yourself":"Partagez vous-même les codes","Player's Voxalike email":"Adresse e-mail du joueur Voxalike","Searching...":"Recherche...","Find player":"Trouver un joueur","Could not find that player.":"Impossible de trouver ce joueur.","Confirm recipient":"Confirmer le destinataire","Open profile":"Ouvrir le profil","This is your account":"Ceci est votre compte","Already has Supporter":"A déjà Supporter","Gift Supporter for US$5":"Cadeau Supporter pour 5 USD","Number of gift codes":"Nombre de codes cadeaux",Total:"Total","Buy {count} gift codes":"Achetez des {count} codes cadeaux","Buy 1 gift code":"Achetez 1 code cadeau","SUPPORTER GIFTS":"CADEAUX DE SUPPORTEURS","{count} unused codes":"{count} codes inutilisés","1 unused code":"1 code inutilisé","Copy all unused":"Copiez tout ce qui n'est pas utilisé","Download unused":"Téléchargement inutilisé","Loading gift codes...":"Chargement des codes cadeaux...","You do not have any gift codes yet.":"Vous n'avez pas encore de codes cadeaux.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"Le paiement est en cours de confirmation. Vos codes cadeaux apparaîtront automatiquement dans le gestionnaire de cadeaux.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"Le paiement est en cours de confirmation. Le cadeau sera livré automatiquement à {name}.","Supporter was delivered to {name}.":"Supporter a été livré à {name}.","the selected player":"le joueur sélectionné","That player already has Supporter.":"Ce joueur possède déjà Supporter.","Use Activate on this account to buy Supporter for yourself.":"Utilisez Activer sur ce compte pour acheter Supporter pour vous-même.","That Voxalike account is no longer available.":"Ce compte Voxalike n'est plus disponible.","Give the code to any player to redeem.":"Donnez le code à n'importe quel joueur pour qu'il l'utilise.","Your gift co
2des":"Vos codes cadeaux",Redeemed:"Racheté","Ready to redeem":"Prêt à racheter","Copy code":"Copier le code","Redeem Supporter code":"Utiliser le code Supporter","Enter Supporter code":"Saisissez le code Supporter","Redeem code":"Utiliser le code","Code redeemed. Supporter is now active.":"Code utilisé. Supporter est désormais actif.","This code is invalid or is not ready yet.":"Ce code n'est pas valide ou n'est pas encore prêt.","This code has already been redeemed.":"Ce code a déjà été utilisé.","Too many redemption attempts. Please wait a few minutes.":"Trop de tentatives de rachat. Veuillez patienter quelques minutes.","Customize style":"Personnaliser le style","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike est géré par QHyralis, LLC, le destinataire du paiement.","One payment. Permanent benefits.":"Un paiement. Avantages permanents.","One-time payment":"Paiement unique","Permanent Supporter badge":"Insigne permanent Supporter","Ad-free on this account":"Sans publicité sur ce compte","Xsolla could not start checkout. Please try again.":"Xsolla n'a pas pu démarrer le paiement. Veuillez réessayer.","Too many checkout attempts. Please wait a few minutes.":"Trop de tentatives de paiement. Veuillez patienter quelques minutes.","Supporter checkout is not available yet.":"Le paiement Supporter n'est pas encore disponible.","Supporter is already active on this account.":"Supporter est déjà actif sur ce compte.","Payment is being confirmed. Refresh in a moment.":"Le paiement est en cours de confirmation. Actualisez dans un instant.","Supporter active":"Supporter actif","Your badge and ad-free access are permanently active.":"Votre badge et votre accès sans publicité sont actifs en permanence.","Loading account...":"Chargement du compte...","Sign in to continue":"Connectez-vous pour continuer","Buy Now":"Acheter maintenant","Payments are processed securely by Xsolla.":"Les paiements sont traités en toute sécurité par Xsolla.","One purchase per account.":"Un achat par compte.","By continuing, you agree to the":"En continuant, vous acceptez les",Terms:"Conditions",and:"et","Privacy Policy":"Politique de confidentialité","Admin Benefits Active":"Avantages administratifs actifs","Contributor Benefits Active":"Avantages des contributeurs actifs","Apply for Contributor":"Demander le statut de contributeur","Admin Active":"Administrateur actif","Contributor Active":"Contributeur actif","Contributor Program":"Programme des contributeurs","Creator Workshop":"Atelier Créateur","Sign out":"Se déconnecter",Community:"Communauté","Main menu":"Menu principal","Single Player":"Un joueur",Multiplayer:"Multijoueur",Mods:"Mods",Settings:"Paramètres","Install App":"Installer l'application","Community Contributor applications":"Demandes de contributeur communautaire",Dismiss:"Rejeter","Dismiss contributor notice":"Rejeter l'avis du contributeur","Limited Alpha Event":"Ãvénement alpha limité","Community Contributor applications are open":"Les candidatures pour les contributeurs de la communauté sont ouvertes","Earn Ad-Free access and an exclusive multiplayer badge.":"Gagnez un accès sans publicité et un badge multijoueur exclusif.","Ends Sep 8, 2026 at 11:59 PM PT":"Se termine le 8 septembre 2026 à 23h59 (heure du Pacifique)","View details":"Afficher les détails",Back:"Retour",Me
2nu:"Menus","New Run":"Nouvelle course","SINGLE PLAYER":"UN JOUEUR",MULTIPLAYER:"MULTIJOUEUR","Preparing Stage":"Ãtape de préparation","My Dub Premiere":"Ma première doublage","Judge Score":"Score du juge","Final Score":"Score final","Dub Stage":"Scène de doublage","Create or Join":"Créer ou rejoindre","Recent Dub":"Doublage récent","PLAYER OPTIONS":"OPTIONS DU JOUEUR","Press F11 for fullscreen.":"Appuyez sur F11 pour le plein écran.","MOD MANAGER":"Gestionnaire de modules","Audio Settings":"Paramètres audio","Control game audio levels.":"Contrôlez les niveaux audio du jeu.","Master Volume":"Volume principal","Sound Effects":"Effets sonores","Background Music":"Musique de fond","Input Device":"Périphérique d'entrée","Voice Processing":"Traitement vocal","Microphone Volume":"Volume du microphone","Changes recording playback and exports. Scoring is unaffected.":"Modifie la lecture et l'exportation de l'enregistrement. Le score nâest pas affecté.",Natural:"Naturel","Browser noise reduction":"Réduction du bruit du navigateur","Clean voice":"Voix claire","Best clarity for headphones and quiet rooms.":"Meilleure clarté pour les écouteurs et les pièces calmes.","Uses only your browser's built-in noise reduction.":"Utilise uniquement la réduction de bruit intégrée à votre navigateur.","No browser or Voxalike voice processing.":"Aucun navigateur ni traitement vocal Voxalike.","Raw microphone active":"Microphone brut actif","Browser and Voxalike voice processing are disabled; no calibration is needed.":"Le navigateur et le traitement vocal Voxalike sont désactivés ; aucun étalonnage nâest nécessaire.","Record a short sample to hear your microphone without voice processing.":"Enregistrez un court échantillon pour entendre votre microphone sans traitement vocal.","Listen to the sample to hear your microphone without voice processing.":"Ãcoutez l'échantillon pour entendre votre microphone sans traitement vocal.","Browser noise reduction active":"Réduction du bruit du navigateur active","Your browser handles noise reduction automatically; no calibration is needed.":"Votre navigateur gère automatiquement la réduction du bruit ; aucun étalonnage nâest nécessaire.","Record a short sample to hear your browser's built-in noise reduction.":"Enregistrez un court extrait pour entendre la réduction de bruit intégrée à votre navigateur.","Listen to the sample to hear your browser's built-in noise reduction.":"Ãcoutez l'extrait pour entendre la réduction de bruit intégrée à votre navigateur.","Play sample":"Lire un extrait","Stronger cleanup for fans, keyboards and noisy rooms.":"Nettoyage plus efficace des ventilateurs, des claviers et des pièces bruyantes.",Default:"Par défaut","Microphone {number}":"Micro {number}","Mic Check":"Vérification du micro","Recording Sync":"Synchronisation de l'enregistrement","Left: earlier. Right: later.":"Gauche : plus tôt. C'est vrai : plus tard.","Voice timing":"Synchronisation de la voix",Earlier:"Plus tôt",Later:"Plus tard","Input Level":"Niveau d'entrée","Stay Silent":"Restez silencieux","Measuring background noise for 2 seconds.":"Mesure du bruit de fond pendant 2 secondes.","Speak Normally":"Parlez normalement","Record your voice at normal volume for 3-5 seconds.":"Enregistrez votre voix à volume normal pendant 3 à 5 secondes.","Stay silent for 2 seconds. Measuring background noise.":"Restez silencieux pendant 2 secondes. Mesurer le bruit de fond.","Speak at your normal volume for 3-5 seconds.":"Parlez à votre volume normal pendant 3 à 5 secondes.","This mic check is applied to future dubbing recordings.":"Cette vérification du micro est appliquée aux futurs enregistrements de doublage.","Compare the samples, then apply this cleanup to future recordings.":"Comparez les échantillons, puis appliquez ce nettoyage aux futurs enregistrements.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"Votre calibrage de micro enregistré est appliqué aux futurs enregistrements de doublage sur ce navigateur.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"Restez silencieux pendant 2 secondes, puis parlez normalement pendant 3 à 5 secondes. Le résultat peut être appliqué au nettoyage du bruit dans le jeu.","Use Mic Check to connect":"Utilisez Mic Check pour vous connecter","Requesting microphone":"Demander un microphone","Microphone Ready":"Microphone prêt","Permission denied":"Autorisation refusée","Microphone Unavailable":"Microphone indisponible","Microphone may be occupied":"Le microphone est peut-être occupé","Selected device unavailable":"Appareil sélectionné indisponible","Device disconnected":"Appareil déconnecté","Calibration saved · mic reconnects when needed":"Calibrage enregistré · le micro se reconnecte en cas de besoin","Microphone could not start":"Le microphone n'a pas pu démarrer","Mic calibration applied":"Calibrage du micro appliqué","Mic calibration not applied":"Calibrage du micro non appliqué","Noise cleanup is active for future recordings.":"La suppression du bruit est active pour les futurs enregistrements.","Run Mic Check and apply it to improve noise cleanup.":"Exécutez Mic Check et appliquez-le pour améliorer le nettoyage du bruit.",Applied:"Appliqué","Ready to apply":"Prêt à postuler","Noise:":"Bruit :","Volume:":"Volume :","Peaks:":"Sommets :",Raw:"Brut",Cleaned:"Nettoyé",Retake:"Reprendre","Apply Mic Check":"Appliquer la vérification du micro","Mic is too loud":"Le micro est trop fort","Backgroun
2d noise is high":"Le bruit de fond est élevé","Voice is quiet":"La voix est calme","Voice is loud":"La voix est forte","Mic sounds ready":"Le micro semble prêt","Start Mic Check":"Démarrer la vérification du micro","Connecting Mic...":"Connexion du micro...","Stop Recording":"Arrêter l'enregistrement",Record:"Enregistrer","Retry Mic":"Réessayer le micro",Replay:"Rejouer","My Take":"Mon avis",Retry:"Réessayer",Next:"Suivant",Finish:"Terminer",Skip:"Sauter","GET READY":"PRÃPAREZ-VOUS",RECORDING:"ENREGISTREMENT","CLIP {current} / {total}":"CLIP {current} / {total}","Jump ahead to another clip":"Passer à un autre clip","Replay original clip":"Rejouer le clip original","Finish pack":"Terminer le pack","Go to next clip":"Aller au clip suivant",Start:"Commencer","Starting...":"Commencer...","Could not start this mod":"Impossible de démarrer ce mod","Copy start error details":"Copier les détails de l'erreur de démarrage","Creator:":"Créateur :","Characters:":"Personnages :","No Dub Mods Yet":"Pas encore de mods de doublage","Subscribe to a pack from the Workshop, then start your game.":"Abonnez-vous à un pack de l'Atelier, puis démarrez votre partie.","Browse Workshop":"Parcourir l'atelier","My Mods":"Mes modules","Refresh Mods":"Actualiser les modules","Refreshing Mods...":"Mods rafraîchissants...","Refreshing...":"Rafraîchissant...","Mods are up to date.":"Les mods sont à jour.","{count} mods":"{count} modules","{count} / syncing Workshop":"{count} / Atelier de synchronisation","{count} / {name} judges in use":"{count} / {name} juges utilisés","{count} / default judges in use":"{count} / juges par défaut utilisés","Local mod import options":"Options d'importation de mods locaux","Import local file":"Importer un fichier local","Dub Mod ZIP":"Mod de doublage ZIP","Dub Mod Folder":"Dossier Dub Mod","Judge Mod ZIP":"Juge Mod ZIP","Search by mod, creator, character, or type":"Rechercher par mod, créateur, personnage ou type","Search mods":"Rechercher des modules","Clear search":"Effacer la recherche","Syncing Workshop subscriptions":"Synchronisation des abonnements à l'atelier","Checking which installed mods belong to your account.":"Vérifier quels mods installés appartiennent à votre compte.","No mods yet":"Pas encore de mods","Subscribe to a pack from the Workshop, then start a game in":"Abonnez-vous à un pack de l'Atelier, puis démarrez une partie dans",Solo:"Solo",or:"ou"," game.":"jeu.",'No mods match "{query}"':'Aucun mod ne correspond à "{query}"',"Clear Search":"Effacer la recherche","View installation progress in Mods":"Afficher la progression de l'installation dans Mods","Vie
2w in Mods":"Afficher dans les modules","Show clip scores":"Afficher les partitions des clips","Reconnect microphone and record":"Rebranchez le microphone et enregistrez","Play your recorded take with background audio":"Jouez votre prise enregistrée avec un fond audio","Play your recorded take":"Jouez votre prise enregistrée","Background volume":"Volume de fond",Background:"Contexte",Cancel:"Annuler","Report Player":"Lecteur de rapports","Mod feedback":"Commentaires sur les mods","Report this mod":"Signaler ce mod","Report reason":"Raison du signalement","Incorrect role information":"Informations de rôle incorrectes","Incorrect language":"Langue incorrecte","Broken or misleading mod":"Mod cassé ou trompeur","Inappropriate content":"Contenu inapproprié","Copyright or rights issue":"Problème de droits d'auteur ou de droits","Spam or abuse":"Spam ou abus","Describe the problem":"Décrivez le problème","Send Report":"Envoyer le rapport","Sending...":"Envoi...","Report received.":"Rapport reçu.","Report could not be submitted.":"Le rapport n'a pas pu être soumis.","Close report dialog":"Fermer la boîte de dialogue du rapport","Reported Player":"Joueur signalé","Report Type":"Type de rapport","Choose a violation type":"Choisissez un type de violation",Description:"Descriptif","Describe what happened so moderators can review the report.":"Décrivez ce qui s'est passé afin que les modérateurs puissent examiner le rapport.","At least 10 characters.":"Au moins 10 caractères.","Submit Report":"Soumettre le rapport","Harassment / Abuse":"Harcèlement / Abus","Hate Speech":"Discours de haine",Spam:"Pourriel","Inappropriate Content":"Contenu inapproprié",Other:"Autre","Clip Scores":"Partitions des clips","{completed} / {total} clips completed":"{completed} / {total} clips terminés",Detail:"Détail","Back to final score":"Retour à la note finale","No playable dub video is available.":"Aucune vidéo doublée lisible nâest disponible.","Play my dubbed work":"Jouer mon travail doublé","PUBLIC MULTIPLAYER":"MULTIJOUEUR PUBLIC","Public Lobby":"Hall d'entrée public","Refresh public rooms":"Rafraîchir les espaces publics","Multiplayer Lobby":"Lobby multijoueur","Refresh rooms":"Rafraîchir les pièces","All Languages":"Toutes les langues",Status:"Statut","All Rooms":"Toutes les chambres","Waiting Only":"En attente seulement","In Progress":"En cours",Results:"Résultats",Waiting:"En attente","No public rooms match these filters.":"Aucune salle publique ne correspond à ces filtres.","Loading public rooms...":"Chargement des salles publiques...","No rooms match these filters.":"Aucune pièce ne correspond à ces filtres.","Loading rooms...":"Salles de chargement...","Room visibility":"Visibilité de la pièce",Private:"Privé","Private Room":"Chambre Privée",Public:"Publique","Room Language":"Langue de la salle","Sign in to create a public room":"Connectez-vous pour créer un espace public","Waiting for host to choose a mod":"En attente que l'hôte choisisse un mod",Join:"Rejoindre","View Profile":"Voir le profil","Host must start: {count}s":"L'hôte doit démarrer : {count}s","Room Chat":"Chat en salle","No messages yet.":"Aucun message pour l'instant.","Message the room":"Envoyez un message à la chambre","Reconnecting chat...":"Reconnexion du chat...","Send message":"Envoyer un message","Open room chat":"Discussion en salle ouverte","Close room chat":"Fermer la discussion en salle","Players online":"Joueurs en ligne","{count} unread messages":"{count} messages non lus","{name} disconnected.":"{name} déconnecté.","{name} reconnected.":"{name} reconnecté.","{name} left the room.":"{name} a quitté la pièce.","This public room has been waiting for 50 minutes.":"Cette salle publique attend depuis 50 minutes.","Player Name":"Nom du joueur","Create Room":"Créer une pièce","Creating...":"Création...","Room Code":"Code de la chambre","Join Room":"Rejoindre la salle","Joining...":"Rejoindre...","Leave Room":"Quitter la pièce","ROOM CODE":"CODE DE LA CHAMBRE","Copy Code":"Copier le code","Copy Invite Link":"Copier le lien d'invitation",Copied:"Copié",Host:"Hôte",Ready:"Prêt",Rendering:"Rendu","Not rendered":"Non rendu","Waiting â click to switch rendering":"En attente â cliquez pour changer de rendu","Not Ready":"Pas prêt","Kick Player":"Joueur de coup de pied","Host actions for {name}":"Actions d'hôte pour {name}","Empty player slot":"Emplacement de joueur vide","Ready Up":"Prêt","Cancel Ready":"Annuler Prêt","Installing room mod":"Installation du mod de salle","Installing room mod {percent}%":"Installation du mod de salle {percent}%","Start Game":"Commencer le jeu","Starting in {count}":"à partir de {count}","How multiplayer mods sync":"Comment les mods multijoueurs se synchronisent","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"Les abonnements à l'atelier s'installent automatiquement pour les joueurs connectés. Les mods locaux doivent être téléchargés et importés par toutes les personnes présentes dans la salle.","Change Mod":"Changer de module","Choose a Mod":"Choisissez un module","Import a mod f
2irst":"Importez d'abord un mod","Multiplayer mode":"Mode multijoueur","Co-op":"Coopérative","Rotate turns or let players claim open clips.":"Faites pivoter les tours ou laissez les joueurs réclamer des clips ouverts.","Everyone records the same clips and compares scores.":"Tout le monde enregistre les mêmes clips et compare les scores.","Co-op Turn Mode":"Mode de tour coopératif","Co-op Assignment":"Affectation coopérative","Rotate turns, claim clips, or claim roles.":"Faites pivoter les tours, revendiquez des clips ou revendiquez des rôles.","Round Robin":"Tournoi à la ronde","Claim Mode":"Mode de réclamation","Role Claim":"Revendication de rôle","Co-op · Role Claim":"Coop · Revendication de rôle","Choose Roles":"Choisissez des rôles","Back to room":"Retour à la chambre","All roles are assigned.":"Tous les rôles sont attribués.","Start Dubbing":"Démarrer le doublage","Waiting for the host to start dubbing.":"En attente que l'hôte commence la copie.","Syncing role selection":"Synchronisation de la sélection des rôles","This mod has no roles to claim":"Ce mod n'a aucun rôle à revendiquer","Claim every role before starting":"Réclamez tous les rôles avant de commencer","Choose your roles":"Choisissez vos rôles","Choose your roles.":"Choisissez vos rôles.",Claim:"Réclamation",Release:"Libération","Returned to lobby.":"Je suis retourné dans le hall.","Could not start role selection.":"Impossible de démarrer la sélection des rôles.","Could not return to lobby.":"Impossible de retourner au lobby.","Could not update role.":"Impossible de mettre à jour le rôle.","Session complete":"Séance terminée","Click a role to claim it. Click again to release it.":"Cliquez sur un rôle pour le revendiquer. Cliquez à nouveau pour le libérer.","Preview lines":"Lignes d'aperçu","Loading lines...":"Chargement des lignes...","LINE PREVIEW":"APERÃU DE LA LIGNE","{count} lines":"{count} lignes","Filter lines by role":"Filtrer les lignes par rôle",All:"Tout",Unassigned:"Non attribué","No lines found for this role.":"Aucune ligne trouvée pour ce rôle.",claimed:"revendiqué","Claim a role before the host starts. You can take more than one.":"Réclamez un rôle avant le démarrage de l'hôte. Vous pouvez en prendre plusieurs.","Randomize Player Order":"Randomiser l'ordre des joueurs","Shuffles the player order once when the game starts, then continues rotating fairly.":"Mélange l'ordre des joueurs une fois au début du jeu, puis continue la rotation de manière équitable.","PVP Format":"Format PvP","Turn Battle":"Tournez la bataille","Parallel Race":"Course parallèle","Players record separately. Faster players get a higher score.":"Les joueurs enregistrent séparément. Les joueurs les plus rapides obtiennent un score plus élevé.","Players take turns recording the same clip before moving to the next one.":"Les joueurs enregistrent à tour de rôle le même clip avant de passer au suivant.","Allow Re-records":"Autoriser les réenregistrements","Player Limit":"Limite de joueurs",Confirm:"Confirmer","On: players can clear their current multiplayer take and record it again before submitting.":"Activé : les joueurs peuvent effacer leur prise multijoueur actuelle et l'enregistrer à nouveau avant de la soumettre.","DUB MOD":"MOD DUB","{count} players in room":"{count} joueurs dans la salle","Score Judging":"Jugement des scores","Judge feedback":"Commentaires des juges","Judge score stage":"Ãtape de notation des juges","Preparing score":"Préparation de la partition","Your {label} is":"Votre {label} est","Final score {score}":"Note finale {score}",pending:"en attente",Continue:"Continuer",Duration:"Durée",Timing:"Calendrier",Energy:"Ãnergie",Pitch:"Emplacement","Perfect Fit":"Ajustement parfait","Good Fit":"Bon ajustement",Uneven:"Inégal","Too Short/Long":"Trop court/long","Locked In":"Enfermé","On Time":"à l'heure",Close:"Fermer","Off Beat":"Contre-temps","Stage Ready":"Prêt pour la scène",Expressive:"Expressif",Solid:"Solide",Flat:"Plat",Uncanny:"Ãtrange","Very Close":"Très proche","Close Enough":"Assez proche",Different:"Différent","Perfect Dub":"Doublage parfait","Viral Performance":"Performance virale","Scene Stealer":"Voleur de scène","Great Take":"Excellente prise","Solid Dub":"Dub solide","Getting There":"S'y rendre","Keep Practicing":"Continuez à pratiquer","Download exp
2orts":"Télécharger les exportations","Download My Work":"Télécharger mon travail","Your browser needs one more tap to play this mod video.":"Votre navigateur a besoin d'un appui supplémentaire pour lire cette vidéo de mod.","Continue Export":"Continuer l'exportation","Video subtitle option":"Option de sous-titres vidéo",Clean:"Nettoyer",Subtitles:"Sous-titres","Download my vertical Shorts video":"Téléchargez ma vidéo Shorts verticaux","Save Shorts":"Enregistrer les courts métrages","My Shorts":"Mon short",Video:"Vidéo","Download my landscape video":"Téléchargez ma vidéo paysage","Save Landscape":"Enregistrer le paysage","My Landscape":"Mon paysage","Download my audio track":"Télécharger ma piste audio","Save Audio":"Enregistrer l'audio","My Audio":"Mon audio",Track:"Suivre","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Gardez cette page ouverte pendant la préparation. Les longues vidéos peuvent prendre plusieurs minutes. Lorsque vous êtes prêt, appuyez sur Enregistrer pour utiliser le menu de sauvegarde de l'iPhone.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"Les exportations sont WebM. Chrome est recommandé pour la lecture ; si vous avez besoin de MP4, veuillez convertir le fichier vous-même.","Share exports":"Partager les exportations",Share:"Partager","Copy share caption":"Copier la légende du partage","Copy caption":"Copier la légende","Rotate your phone":"Faites pivoter votre téléphone","Landscape gives the video and recording controls more room.":"Le paysage donne plus dâespace aux commandes vidéo et dâenregistrement.","Stay in portrait":"Restez en portrait","Previous run ended unexpectedly":"L'exécution précédente s'est terminée de manière inattendue","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike rechargé alors qu'un jeu était actif. Copiez ce rapport si l'interruption se reproduit.","Copy crash report":"Copier le rapport d'erreur","Dismiss crash report":"Ignorer le rapport d'erreur","Page interrupted":"Page interrompue","Your room and locally saved recordings can be restored after reloading.":"Votre salle et les enregistrements enregistrés localement peuvent être restaurés après le rechargement.","Reload Voxalike":"Recharger Voxalike","ROOM CLOSING SOON":"FERMETURE DE LA SALLE BIENTÃT","This room has been waiting for 10 minutes.":"Cette pièce attend depuis 10 minutes.","Start the game within the next 10 minutes or the room will close automatically.":"Démarrez le jeu dans les 10 prochaines minutes ou la salle se fermera automatiquement.","Room closed because no game started within 20 minutes.":"Salle fermée car aucun jeu n'a commencé dans les 20 minutes.","Room closed because the host did not reconnect within 5 minutes.":"Salle fermée car l'hôte ne s'est pas reconnecté dans les 5 minutes.","Wait for disconnected players to reconnect":"Attendez que les joueurs déconnectés se reconnectent",Reconnecting:"Reconnexion","Game server updated":"Serveur de jeu mis à jour","Connection interrupted":"Connexion interrompue","Syncing the latest game server and restoring your room.":"Synchronisation du dernier serveur de jeu et restauration de votre salle.","Your connection dropped. Trying to reconnect to your room.":"Votre connexion a été interrompue. J'essaie de me reconnecter à votre chambre."},it:{"Interface Language":"Linguaggio dell'interfaccia","Choose the language used by the game interface.":"Scegli la lingua utilizzata dall'interfaccia di gioco.",Language:"Lingua",PLAY:"GIOCA",NEWS:"NOVITÃ","DEVELOPER NEWS":"NOTIZIE DAGLI SVILUPPATORI","Voxalike News":"Voxalike Novità ",GUIDE:"GUIDA",WORKSHOP:"LABORATORIO",SUPPORTER:"SOSTENITORE",SUPPORT:"SUPPORTO","Sign in":"Accedi","Account menu":"Menù conto",Notifications:"Notifiche",Profile:"Profilo","Account details":"Dettagli del conto","Edit Profile":"Modifica profilo",Studio:"Studio","Support Again":"Supporta ancora","Become a Supporter":"Diventa un Supp
2orter","Support Voxalike":"Supporto Voxalike","Voxalike Player":"Voxalike Giocatore","Supporter profile preview":"Anteprima del profilo Supporter","Profile preview":"Anteprima del profilo","6 name colors":"6 colori dei nomi","3 avatar frames":"3 cornici per avatar","4 avatar frames":"4 cornici per avatar","3 avatar frames + 1 animated bonus frame":"3 cornici avatar + 1 cornice animata bonus","Try on {frame}":"Prova {frame}","Supporter purchase":"Supporter acquisto","one time":"una volta","No subscription":"Nessun abbonamento","Animated Supporter badge":"Badge Supporter animato","Checkout could not start. Please try again.":"Impossibile avviare il pagamento. Per favore riprova.","Checkout was canceled. Nothing was charged.":"Il pagamento è stato annullato. Non è stato addebitato nulla.","Payment is being confirmed. Your benefits will appear here automatically.":"Il pagamento è in fase di conferma. I tuoi vantaggi verranno visualizzati qui automaticamente.","Buy Supporter for US$5":"Acquista Supporter per US $ 5","Gift code checkout is not available yet.":"Il pagamento del codice regalo non è ancora disponibile.","Payment is being confirmed. Your gift code will appear here automatically.":"Il pagamento è in fase di conferma. Il tuo codice regalo apparirà qui automaticamente.","Buy Supporter or a gift code":"Acquista Supporter o un codice regalo","Activate on this account":"Attiva su questo account","Supporter benefits start after payment.":"I vantaggi di Supporter iniziano dopo il pagamento.","Buy a Supporter gift code":"Acquista un codice regalo Supporter.","Give Supporter":"Regala Supporter","Manage gift codes":"Gestisci i codici regalo","SUPPORTER GIFT":"REGALO SOSTENITORE","Gift delivery method":"Metodo di consegna del regalo","Send to a player":"Invia a un giocatore","Delivered directly to their account":"Consegnato direttamente sul loro conto","Buy gift codes":"Acquista codici regalo","Share the codes yourself":"Condividi tu stesso i codici","Player's Voxalike email":"E-mail Voxalike del giocatore","Searching...":"Ricerca...","Find player":"Trova giocatore","Could not find that player.":"Impossibile trovare quel giocatore.","Confirm recipient":"Conferma il destinatario","Open profile":"Apri profilo","This is your account":"Questo è il tuo conto","Already has Supporter":"Ha già Supporter","Gift Supporter for US$5":"Regala Supporter per US$ 5","Number of gift codes":"Numero di codici regalo",Total:"Totale","Buy {count} gift codes":"Acquista {count} codici regalo","Buy 1 gift code":"Acquista 1 codice regalo","SUPPORTER GIFTS":"REGALI PER I SOSTENITORI","{count} unused codes":"{count} codici non utilizzati","1 unused code":"1 codice non utilizzato","Copy all unused":"Copia tutto inutilizzato","Download unused":"Scarica inutilizzato","Loading gift codes...":"Caricamento codici regalo...","You do not have any gift codes yet.":"Non hai ancora nessun codice regalo.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"Il pagamento è in fase di conferma. I tuoi codici regalo appariranno automaticamente nel gestore dei regali.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"Il pagamento è in fase di conferma. Il regalo verrà consegnato a {name} automaticamente.","Supporter was delivered to {name}.":"Supporter è stato consegnato a {name}.","the selected player":"il giocatore selezionato","That player already has Supporter.":"Quel giocatore ha già Supporter.","Use Activate on this account to buy Supporter for yourself.":"Utilizza Attiva su questo account per acquistare Supporter per te stesso.","That Voxalike account is no longer available.":"L'account Voxalike non è più disponibile.","Give the code to any player to redeem.":"Dai il codice a qualsiasi giocatore per riscattarlo.","Your gift codes":"I tuoi codici regalo",Redeemed:"Redento","Ready to redeem":"Pronto a riscattare","Copy code":"Copia il codice","Redeem Supporter code":"Riscatta il codice Supporter.","Enter Supporter code":"Inserisci il codice Supporter.","Redeem code":"Riscatta il
2codice","Code redeemed. Supporter is now active.":"Codice riscattato. Supporter è ora attivo.","This code is invalid or is not ready yet.":"Questo codice non è valido o non è ancora pronto.","This code has already been redeemed.":"Questo codice è già stato riscattato.","Too many redemption attempts. Please wait a few minutes.":"Troppi tentativi di riscatto. Per favore attendi qualche minuto.","Customize style":"Personalizza lo stile","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike è gestito da QHyralis, LLC, il destinatario del pagamento.","One payment. Permanent benefits.":"Un pagamento. Benefici permanenti.","One-time payment":"Pagamento una tantum","Permanent Supporter badge":"Badge Supporter permanente","Ad-free on this account":"Senza pubblicità su questo account","Xsolla could not start checkout. Please try again.":"Xsolla non è riuscito ad avviare il pagamento. Per favore riprova.","Too many checkout attempts. Please wait a few minutes.":"Troppi tentativi di pagamento. Per favore attendi qualche minuto.","Supporter checkout is not available yet.":"Il pagamento Supporter non è ancora disponibile.","Supporter is already active on this account.":"Supporter è già attivo su questo account.","Payment is being confirmed. Refresh in a moment.":"Il pagamento è in fase di conferma. Aggiorna in un attimo.","Supporter active":"Supporter attivo","Your badge and ad-free access are permanently active.":"Il tuo badge e l'accesso senza pubblicità sono permanentemente attivi.","Loading account...":"Caricamento account...","Sign in to continue":"Accedi per continuare","Buy Now":"Acquista ora","Payments are processed securely by Xsolla.":"I pagamenti vengono elaborati in modo sicuro da Xsolla.","One purchase per account.":"Un acquisto per account.","By continuing, you agree to the":"Continuando accetti i",Terms:"Termini",and:"e","Privacy Policy":"Informativa sulla privacy","Admin Benefits Active":"Vantaggi per l'amministratore attivi","Contributor Benefits Active":"Vantaggi per i contributori attivi","Apply for Contributor":"Candidati per Collaboratore","Admin Active":"Amministratore attivo","Contributor Active":"Collaboratore attivo","Contributor Program":"Programma per contributori","Creator Workshop":"Laboratorio del Creatore","Sign out":"Esci",Community:"Comunità ","Main menu":"Menù principale","Single Player":"Giocatore singolo",Multiplayer:"Multigiocatore",Mods:"Mod",Settings:"Impostazioni","Install App":"Installa l'app","Community Contributor applications":"Applicazioni dei collaboratori della community",Dismiss:"Ignora","Dismiss contributor notice":"Ignora avviso collaboratore","Limited Alpha Event":"Evento Alpha limitato","Community Contributor applications are open":"Le candidature per i collaboratori della community sono aperte","Earn Ad-Free access and an exclusive multiplayer badge.":"Ottieni l'accesso senza pubblicità e un esclusivo badge multiplayer.","Ends Sep 8, 2026 at 11:59 PM PT":"Termina l'8 settembre 2026 alle 23:59 PT","View details":"Visualizza i dettagli",Back:"Indietro",Menu:"Menù","New Run":"Nuova corsa","SINGLE PLAYER":"GIOCATORE SINGOLO",MULTIPLAYER:"MULTIGIOCATORE","Preparing Stage":"Fase di preparazione","My Dub Premiere":"La mia prima dub","Judge Score":"Punteggio del giudice","Final Score":"Punteggio finale","Dub Stage":"Fase di doppiaggio","Create or Join":"Crea o partecipa","Recent Dub":"Duplicazione recente","PLAYER OPTIONS":"OPZIONI DEL GIOCATORE","Press F11 for fullscreen.":"Premi F11 per lo schermo intero.","MOD MANAGER":"RESPONSABILE MOD","Audio Settings":"Impostazioni audio","Control game audio levels.":"Controlla i livelli audio del gioco.","Master Volume":"Volume principale","Sound Effects":"Effetti sonori","Background Music":"Musica di sottofondo","Input Device":"Dispositivo di input","Voice Processing":"Elaborazione vocale","Microphone Volume":"Volume del microfono","Changes recording playback and exports. Scoring is unaffected.":"Modifica la riproduzione e l'esportazione della registrazione. Il punteggio non viene influenzato.",Natural:"Naturale","Browser noise reduction":"Riduzione del rumore del browser","Clean voice":"Voce pulita","Best clarity for headphones and quiet rooms.":"La migliore chiarezza per cuffie e stanze silenziose.","Uses only your browser's built-in noise reduction.":"Utilizza solo la riduzione del rumore integrata nel tuo browser.","No browser or Voxalike voice processing.":"Nessun browser o elaborazione vocale Voxalike","Raw microphone active":"Microfono grezzo attivo","Browser and Voxalike voice processing are disabled; no calibration is needed.":"Il browser e l'elaborazione vocale Voxalike sono disabilitati; non è necessaria alcuna calibrazione.","Record a short sample to hear your microphone without voice processing.":"Registra un breve campione per ascoltare il tuo microfono senza elaborazione vocale.","Listen to the sample to hear your microphone without voice processing.":"Ascolta l'esempio per sentire il tuo microfono senza elaborazione vocale.","Browser noise reduction active":"Riduzione del rumore del browser attiva","Your browser handles noise reduction automatically; no calibration is needed.":"Il tuo browser gestisce automaticamente la riduzione del rumore; non è necessaria alcuna calibrazione.","Record a short sample to hear your browser's built-in noise reduction.":"Registra un breve campione per ascoltare la riduzione del rumore integrata nel tuo browser.","Listen to the sample to hear your browser's built-in noise reduction.":"Ascolta l'esempio per ascoltare la riduzione del rumore integrata nel tuo browser.","Play
2sample":"Riproduci campione","Stronger cleanup for fans, keyboards and noisy rooms.":"Pulizia più efficace per ventole, tastiere e stanze rumorose.",Default:"Predefinito","Microphone {number}":"Microfono {number}","Mic Check":"Controllo del microfono","Recording Sync":"Sincronizzazione della registrazione","Left: earlier. Right: later.":"A sinistra: prima. Giusto: più tardi.","Voice timing":"Tempistica vocale",Earlier:"In precedenza",Later:"Più tardi","Input Level":"Livello di ingresso","Stay Silent":"Resta in silenzio","Measuring background noise for 2 seconds.":"Misurazione del rumore di fondo per 2 secondi.","Speak Normally":"Parla normalmente","Record your voice at normal volume for 3-5 seconds.":"Registra la tua voce a volume normale per 3-5 secondi.","Stay silent for 2 seconds. Measuring background noise.":"Rimani in silenzio per 2 secondi. Misurazione del rumore di fondo.","Speak at your normal volume for 3-5 seconds.":"Parla al volume normale per 3-5 secondi.","This mic check is applied to future dubbing recordings.":"Questo controllo del microfono viene applicato alle future registrazioni di doppiaggio.","Compare the samples, then apply this cleanup to future recordings.":"Confronta i campioni, quindi applica questa pulizia alle registrazioni future.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"La calibrazione del microfono salvata viene applicata alle future registrazioni di doppiaggio su questo browser.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"Rimani in silenzio per 2 secondi, poi parla normalmente per 3-5 secondi. Il risultato può essere applicato alla pulizia del rumore nel gioco.","Use Mic Check to connect":"Utilizzare Mic Check per connettersi","Requesting microphone":"Richiesta microfono","Microphone Ready":"Microfono pronto","Permission denied":"Autorizzazione negata","Microphone Unavailable":"Microfono non disponibile","Microphone may be occupied":"Il microfono potrebbe essere occupato","Selected device unavailable":"Dispositivo selezionato non disponibile","Device disconnected":"Dispositivo disconnesso","Calibration saved · mic reconnects when needed":"Calibrazione salvata · Il microfono si riconnette quando necessario","Microphone could not start":"Impossibile avviare il microfono","Mic calibration applied":"Calibrazione del microfono applicata","Mic calibration not applied":"Calibrazione del microfono non applicata","Noise cleanup is active for future recordings.":"La pulizia del rumore è attiva per le registrazioni future.","Run Mic Check and apply it to improve noise cleanup.":"Esegui Mic Check e applicalo per migliorare la pulizia del rumore.",Applied:"Applicato","Ready to apply":"Pronto per candidarsi","Noise:":"Rumore:","Volume:":"Volume:","Peaks:":"Picchi:",Raw:"Crudo",Cleaned:"Pulito",Retake:"Riprendere","Apply Mic Check":"Applica controllo microfono","Mic is too loud":"Il microfono è troppo forte","Background noise is high":"Il rumore di fondo è elevato","Voice is quiet":"La voce è tranquilla","Voice is loud":"La voce è forte","Mic sounds ready":"Il microfono sembra pronto","Start Mic Check":"Avvia il controllo del microfono","Connecting Mic...":"Collegamento del microfono...","Stop Recording":"Interrompi la registrazione",Record:"Registra","Retry Mic":"Riprova microfono",Replay:"Riproduci","My Take":"La mia opinione",Retry:"Riprova",Next:"Avanti",Finish:"Finisci",Skip:"Salta","GET READY":"PREPARATI",RECORDING:"REGISTRAZIONE","CLIP {current} / {total}":"CLIP {current} / {total}","Jump ahead to another clip":"Vai avanti a un'altra clip","Replay original clip":"Riproduci la clip originale","Finish pack":"Pacchetto finito","Go to next clip":"Vai alla clip successiva",Start:"Inizia","Starting...":"A partire...","Could not start this mod":"Impossibile avviare questa mod","Copy start error details":"Copia i dettagli dell'errore di avvio","Creator:":"Creatore:","Characters:":"Personaggi:","No Dub Mods Yet":"Ancora nessun mod doppiaggio","Subscribe to a pack from the Workshop, then start your game.":"Iscriviti a un pacchetto del Workshop, quindi avvia il gi
2oco.","Browse Workshop":"Sfoglia Workshop","My Mods":"I miei Mod","Refresh Mods":"Aggiorna mod","Refreshing Mods...":"Aggiornamento delle mod...","Refreshing...":"Rinfrescante...","Mods are up to date.":"Le mod sono aggiornate.","{count} mods":"{count} mod","{count} / syncing Workshop":"{count} / sincronizzazione Workshop","{count} / {name} judges in use":"{count} / {name} Judge in uso","{count} / default judges in use":"{count}/giudici predefiniti in uso","Local mod import options":"Opzioni di importazione mod locale","Import local file":"Importa file locale","Dub Mod ZIP":"Mod doppiatura ZIP","Dub Mod Folder":"Cartella Dub Mod","Judge Mod ZIP":"Mod. giudice ZIP","Search by mod, creator, character, or type":"Cerca per mod, creatore, personaggio o tipo","Search mods":"Cerca mod","Clear search":"Cancella ricerca","Syncing Workshop subscriptions":"Sincronizzazione degli abbonamenti al Workshop","Checking which installed mods belong to your account.":"Verifica quali mod installate appartengono al tuo account.","No mods yet":"Nessuna mod ancora","Subscribe to a pack from the Workshop, then start a game in":"Iscriviti a un pacchetto dal Workshop, quindi avvia una partita",Solo:"Solo",or:"o"," game.":"gioco.",'No mods match "{query}"':'Nessuna mod corrisponde a "{query}"',"Clear Search":"Cancella ricerca","View installation progress in Mods":"Visualizza l'avanzamento dell'installazione in Mods","Vie
2w in Mods":"Visualizza in Mod","Show clip scores":"Mostra i punteggi delle clip","Reconnect microphone and record":"Ricollegare il microfono e registrare","Play your recorded take with background audio":"Riproduci la tua ripresa registrata con audio di sottofondo","Play your recorded take":"Riproduci la tua ripresa registrata","Background volume":"Volume di fondo",Background:"Sfondo",Cancel:"Annulla","Report Player":"Segnala giocatore","Mod feedback":"Feedback del mod","Report this mod":"Segnala questo mod","Report reason":"Segnala il motivo","Incorrect role information":"Informazioni sul ruolo errate","Incorrect language":"Lingua errata","Broken or misleading mod":"Mod rotto o fuorviante","Inappropriate content":"Contenuti inappropriati","Copyright or rights issue":"Problema di copyright o diritti","Spam or abuse":"Spam o abuso","Describe the problem":"Descrivi il problema","Send Report":"Invia rapporto","Sending...":"Invio...","Report received.":"Rapporto ricevuto.","Report could not be submitted.":"Impossibile inviare il rapporto.","Close report dialog":"Chiudi la finestra di dialogo del rapporto","Reported Player":"Giocatore segnalato","Report Type":"Tipo di rapporto","Choose a violation type":"Scegli un tipo di violazione",Description:"Descrizione","Describe what happened so moderators can review the report.":"Descrivi cosa è successo in modo che i moderatori possano esaminare la segnalazione.","At least 10 characters.":"Almeno 10 caratteri.","Submit Report":"Invia rapporto","Harassment / Abuse":"Molestie/Abusi","Hate Speech":"Discorso di odio",Spam:"Spam","Inappropriate Content":"Contenuti inappropriati",Other:"Altro","Clip Scores":"Clip punteggi","{completed} / {total} clips completed":"{completed} / {total} clip completati",Detail:"Dettaglio","Back to final score":"Torniamo al punteggio finale","No playable dub video is available.":"Non è disponibile alcun video doppiato riproducibile.","Play my dubbed work":"Riproduci il mio lavoro doppiato","PUBLIC MULTIPLAYER":"MULTIGIOCATORE PUBBLICO","Public Lobby":"Lobby pubblica","Refresh public rooms":"Aggiorna le stanze pubbliche","Multiplayer Lobby":"Lobby multigiocatore","Refresh rooms":"Aggiorna le stanze","All Languages":"Tutte le lingue",Status:"Stato","All Rooms":"Tutte le stanze","Waiting Only":"Solo in attesa","In Progress":"In corso",Results:"Risultati",Waiting:"In attesa","No public rooms match these filters.":"Nessuna stanza pubblica corrisponde a questi filtri.","Loading public rooms...":"Caricamento delle sale pubbliche...","No rooms match these filters.":"Nessuna stanza corrisponde a questi filtri.","Loading rooms...":"Caricamento stanze...","Room visibility":"Visibilità della stanza",Private:"Privato","Private Room":"Stanza privata",Public:"Pubblico","Room Language":"Linguaggio della stanza","Sign in to create a public room":"Accedi per creare una stanza pubblica","Waiting for host to choose a mod":"In attesa che l'host scelga una mod",Join:"Partecipa","View Profile":"Visualizza profilo","Host must start: {count}s":"L'host deve iniziare: {count}s","Room Chat":"Chat in camera","No messages yet.":"Nessun messaggio ancora.","Message the room":"Invia un messaggio alla stanza","Reconnecting chat...":"Riconnessione della chat...","Send message":"Invia messaggio","Open room chat":"Apri la chat nella stanza","Close room chat":"Chiudi la chat nella stanza","Players online":"Giocatori in linea","{count} unread messages":"{count} messaggi non letti","{name} disconnected.":"{name} disconnesso.","{name} reconnected.":"{name} si è riconnesso.","{name} left the room.":"{name} ha lasciato la stanza.","This public room has been waiting for 50 minutes.":"Questa sala pubblica aspetta da 50 minuti.","Player Name":"Nome del giocatore","Create Room":"Crea stanza","Creating...":"Creazione...","Room Code":"Codice della camera","Join Room":"Unisciti alla stanza","Joining...":"Partecipazione...","Leave Room":"Lascia la stanza","ROOM CODE":"CODICE CAMERA","Copy Code":"Copia codice","Copy Invite Link":"Copia il collegamento di invito",Copied:"Copiato",Host:"Ospite",Ready:"Pronto",Rendering:"Rendering","Not rendered":"Non reso","Waiting â click to switch rendering":"In attesa: fare clic per cambiare rendering","Not Ready":"Non pronto","Kick Player":"Giocatore di calci","Host actions for {name}":"Azioni dell'host per {name}","Empty player slot":"Slot giocatore vuoto","Ready Up":"Pronti","Cancel Ready":"Annulla Pronto","Installing room mod":"Installazione camera mod","Installing room mod {percent}%":"Installazione mod stanza {percent}
2%","Start Game":"Inizia il gioco","Starting in {count}":"A partire da {count}","How multiplayer mods sync":"Come si sincronizzano le mod multiplayer","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"Gli abbonamenti al workshop si installano automaticamente per i giocatori che hanno effettuato l'accesso. Le mod locali devono essere scaricate e importate da tutti i presenti nella stanza.","Change Mod":"Cambia mod","Choose a Mod":"Scegli una modalità ","Import a mod first":"Importa prima una mod","Multiplayer mode":"Modalità multigiocatore","Co-op":"Cooperativa","Rotate turns or let players claim open clips.":"Ruota i turni o lascia che i giocatori rivendichino clip aperte.","Everyone records the same clips and compares scores.":"Tutti registrano le stesse clip e confrontano i punteggi.","Co-op Turn Mode":"Modalità a turni cooperativi","Co-op Assignment":"Assegnazione cooperativa","Rotate turns, claim clips, or claim roles.":"Ruota i turni, rivendica clip o rivendica ruoli.","Round Robin":"Girotondo","Claim Mode":"Modalità di reclamo","Role Claim":"Rivendicazione di ruolo","Co-op · Role Claim":"Cooperativa · Rivendicazione del ruolo","Choose Roles":"Scegli Ruoli","Back to room":"Ritorno in camera","All roles are assigned.":"Tutti i ruoli sono assegnati.","Start Dubbing":"Inizia la duplicazione","Waiting for the host to start dubbing.":"In attesa che l'ospite inizi il doppiaggio.","Syncing role selection":"Sincronizzazione della selezione del ruolo","This mod has no roles to claim":"Questa mod non ha ruoli da rivendicare","Claim every role before starting":"Rivendica ogni ruolo prima di iniziare","Choose your roles":"Scegli i tuoi ruoli","Choose your roles.":"Scegli i tuoi ruoli.",Claim:"Richiedi",Release:"Rilascio","Returned to lobby.":"Ritornato nella lobby.","Could not start role selection.":"Impossibile avviare la selezione del ruolo.","Could not return to lobby.":"Impossibile tornare alla lobby.","Could not update role.":"Impossibile aggiornare il ruolo.","Session complete":"Sessione completata","Click a role to claim it. Click again to release it.":"Fare clic su un ruolo per rivendicarlo. Fare di nuovo clic per rilasciarlo.","Preview lines":"Linee di anteprima","Loading lines...":"Caricamento righe...","LINE PREVIEW":"ANTEPRIMA LINEA","{count} lines":"{count} righe","Filter lines by role":"Filtra le righe per ruolo",All:"Tutto",Unassigned:"Non assegnato","No lines found for this role.":"Nessuna riga trovata per questo ruolo.",claimed:"affermato","Claim a role before the host starts. You can take more than one.":"Rivendica un ruolo prima che l'host inizi. Puoi prenderne più di uno.","Randomize Player Order":"Randomizza l'ordine dei giocatori","Shuffles the player order once when the game starts, then continues rotating fairly.":"Mescola l'ordine dei giocatori una volta all'inizio del gioco, poi continua a ruotare equamente.","PVP Format":"Formato PvP","Turn Battle":"Trasforma la battaglia","Parallel Race":"Gara parallela","Players record separately. Faster players get a higher score.":"I giocatori registrano separatamente. I giocatori più veloci ottengono un punteggio più alto.","Players take turns recording the same clip before moving to the next one.":"I giocatori, a turno, registrano la stessa clip prima di passare a quella successiva.","Allow Re-records":"Consenti nuove registrazioni","Player Limit":"Limite del giocatore",Confirm:"Conferma","On: players can clear their current multiplayer take and record it again before submitting.":"On: i giocatori possono cancellare la loro ripresa multiplayer attuale e registrarla di nuovo prima di inviarla.","DUB MOD":"DOPPIO MOD","{count} players in room":"{count} giocatori nella stanza","Score Judging":"Valutazione del punteggio","Judge feedback":"Feedback del giudice","Judge score stage":"Fase del punteggio del giudice","Preparing score":"Preparazione della partitura","Your {label} is":"Il tuo {label} lo è","Final score {score}":"Punteggio finale {score}",pending:"in sospeso",Continue:"Continua",Duration:"Durata",Timing:"Tempistica",Energy:"Energia",Pitch:"Passo","Perfect Fit":"Vestibilità perfetta","Good Fit":"Buona vestibilità ",Uneven:"Irregolare","Too Short/Long":"Troppo corto/lungo","Locked In":"Bloccato","On Time":"In tempo",Close:"Chiudi","Off Beat":"Fuori dal ritmo","Stage Ready":"Palco pronto",Expressive:"Espressivo",Solid:"Solido",Flat:"Piatto",Uncanny:"Incredibile","Very Close"
2:"Molto vicino","Close Enough":"Abbastanza vicino",Different:"Diverso","Perfect Dub":"Doppiaggio perfetto","Viral Performance":"Prestazioni virali","Scene Stealer":"Ladro di scene","Great Take":"Ottima presa","Solid Dub":"Doppiaggio solido","Getting There":"Come arrivare","Keep Practicing":"Continua a esercitarti","Download exports":"Scarica le esportazioni","Download My Work":"Scarica Il mio lavoro","Your browser needs one more tap to play this mod video.":"Il tuo browser ha bisogno di un altro tocco per riprodurre questo video mod.","Continue Export":"Continua l'esportazione","Video subtitle option":"Opzione sottotitoli video",Clean:"Pulito",Subtitles:"Sottotitoli","Download my vertical Shorts video":"Scarica il mio video Shorts verticali","Save Shorts":"Salva pantaloncini","My Shorts":"I miei pantaloncini",Video:"Video","Download my landscape video":"Scarica il mio video sul paesaggio","Save Landscape":"Salva paesaggio","My Landscape":"Il mio paesaggio","Download my audio track":"Scarica la mia traccia audio","Save Audio":"Salva audio","My Audio":"Il mio audio",Track:"Traccia","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Tieni aperta questa pagina durante la preparazione. I video lunghi possono richiedere diversi minuti. Quando sei pronto, tocca Salva per utilizzare il menu di salvataggio dell'iPhone.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"Le esportazioni sono WebM. Per la riproduzione si consiglia Chrome; se hai bisogno di MP4, converti tu stesso il file.","Share exports":"Condividere le esportazioni",Share:"Condividi","Copy share caption":"Copia didascalia di condivisione","Copy caption":"Copia didascalia","Rotate your phone":"Ruota il telefono","Landscape gives the video and recording controls more room.":"L'orientamento orizzontale offre più spazio ai controlli video e di registrazione.","Stay in portrait":"Rimani in ritratto","Previous run ended unexpectedly":"L'esecuzione precedente è terminata in modo imprevisto","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike ricaricato mentre un gioco era attivo. Copia questo rapporto se l'interruzione si ripete.","Copy crash report":"Copia il rapporto sugli arresti anomali","Dismiss crash report":"Ignora la segnalazione di arresto anomalo","Page interrupted":"Pagina interrotta","Your room and locally saved recordings can be restored after reloading.":"La tua stanza e le registrazioni salvate localmente possono essere ripristinate dopo il ricaricamento.","Reload Voxalike":"Ricarica Voxalike","ROOM CLOSING SOON":"CHIUSURA PRESTO DELLA CAMERA","This room has been waiting for 10 minutes.":"Questa stanza aspetta da 10 minuti.","Start the game within the next 10 minutes or the room will close automatically.":"Inizia il gioco entro i prossimi 10 minuti altrimenti la stanza si chiuderà automaticamente.","Room closed because no game started within 20 minutes.":"Sala chiusa perché nessuna partita è iniziata entro 20 minuti.","Room closed because the host did not reconnect within 5 minutes.":"Stanza chiusa perché l'host non si è ricollegato entro 5 minuti.","Wait for disconnected players to reconnect":"Attendi che i giocatori disconnessi si riconnettano",Reconnecting:"Riconnessione","Game server updated":"Server di gioco aggiornato","Connection interrupted":"Connessione interrotta","Syncing the latest game server and restoring your room.":"Sincronizzazione dell'ultimo server di gioco e ripristino della stanza.","Your connection dropped. Trying to reconnect to your room.":"La tua connessione si è interrotta. Sto cercando di riconnettermi alla tua stanza."},es:{"Interface Language":"Idioma de la interfaz","Choose the language used by the game interface.":"Elige el idioma utilizado por la interfaz del juego.",Language:"Idioma",PLAY:"JUGAR",NEWS:"NOTICIAS","DEVELOPER NEWS":"NOTICIAS DEL DESARROLLADOR","Voxalike News":"Voxalike Noticias",GUIDE:"GUÃA",WORKSHOP:"TALLER",SUPPORTER:"PARTIDARIO",SUPPORT:"SOPORTE","Sign in":"Iniciar sesión","Account menu":"Menú de cuenta",Notifications:"Notificaciones",Profile:"Perfil","Account details":"Detalles de la cuenta","Edit Profile":"Editar perfil",Studio:"Estudio","Support Again":"Soporte nuevamente","Become a Supporter":"Conviértete en Supporter","Support Voxalike":"Soporte Voxalike","Voxalike Player":"
2Voxalike Jugador","Supporter profile preview":"Supporter vista previa del perfil","Profile preview":"Vista previa del perfil","6 name colors":"6 colores de nombre","3 avatar frames":"3 marcos de avatar","4 avatar frames":"4 marcos de avatar","3 avatar frames + 1 animated bonus frame":"3 marcos de avatar + 1 marco animado extra","Try on {frame}":"Probar {frame}","Supporter purchase":"Supporter compra","one time":"una vez","No subscription":"Sin suscripción","Animated Supporter badge":"Insignia Supporter animada","Checkout could not start. Please try again.":"No se pudo iniciar el proceso de pago. Por favor inténtalo de nuevo.","Checkout was canceled. Nothing was charged.":"El pago fue cancelado. No se cobró nada.","Payment is being confirmed. Your benefits will appear here automatically.":"Se está confirmando el pago. Tus beneficios aparecerán aquà automáticamente.","Buy Supporter for US$5":"Compra Supporter por US$5","Gift code checkout is not available yet.":"El pago con código de regalo aún no está disponible.","Payment is being confirmed. Your gift code will appear here automatically.":"Se está confirmando el pago. Tu código de regalo aparecerá aquà automáticamente.","Buy Supporter or a gift code":"Compra Supporter o un código de regalo","Activate on this account":"Activar en esta cuenta","Supporter benefits start after payment.":"Supporter los beneficios comienzan después del pago.","Buy a Supporter gift code":"Compra un código de regalo Supporter","Give Supporter":"Dar Supporter","Manage gift codes":"Administrar códigos de regalo","SUPPORTER GIFT":"REGALO DE APOYO","Gift delivery method":"Método de entrega de regalo","Send to a player":"Enviar a un jugador","Delivered directly to their account":"Entregado directamente a su cuenta","Buy gift codes":"Comprar códigos de regalo","Share the codes yourself":"Comparte los códigos tú mismo","Player's Voxalike email":"Correo electrónico Voxalike del jugador","Searching...":"Buscando...","Find player":"encontrar jugador","Could not find that player.":"No se pudo encontrar ese jugador.","Confirm recipient":"Confirmar destinatario","Open profile":"Abrir perfil","This is your account":"Esta es tu cuenta","Already has Supporter":"Ya tiene Supporter","Gift Supporter for US$5":"Regalo Supporter por US$5","Number of gift codes":"Número de códigos de regalo",Total:"totales","Buy {count} gift codes":"Compra {count} códigos de regalo","Buy 1 gift code":"Compra 1 código de regalo","SUPPORTER GIFTS":"REGALOS DE APOYADORES","{count} unused codes":"{count} códigos no utilizados","1 unused code":"1 código no utilizado","Copy all unused":"Copiar todo lo no utilizado","Download unused":"Descargar sin usar","Loading gift codes...":"Cargando códigos de regalo...","You do not have any gift codes yet.":"Aún no tienes ningún código de regalo.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"Se está confirmando el pago. Tus códigos de regalo aparecerán automáticamente en el administrador de regalos.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"Se está confirmando el pago. El regalo se entregará a {name} automáticamente.","Supporter was delivered to {name}.":"Supporter fue entregado a {name}.","the selected player":"el jugador seleccionado","That player already has Supporter.":"Ese jugador ya tiene Supporter.","Use Activate on this account to buy Supporter for yourself.":"Utilice Activar en esta cuenta para comprar Supporter para usted.","That Voxalike account is no longer available.":"Esa cuenta Voxalike ya no está disponible.","Give the code to any player to redeem.":"Dale el código a cualquier jugador para que lo canjee.","Your gift codes":"Tus códigos de regalo",Redeemed:"Redimido","Ready to redeem":"Listo para canjear","Copy code":"Copiar código","Redeem Supporter code":"Canjear código Supporter","Enter Supporter code":"Ingresa el código Supporter","Redeem code":"Canjear código","Code redeemed. Supporter is now active.":"Código canjeado. Supporter ahora está activo.","This code is invali
2d or is not ready yet.":"Este código no es válido o aún no está listo.","This code has already been redeemed.":"Este código ya ha sido canjeado.","Too many redemption attempts. Please wait a few minutes.":"Demasiados intentos de redención. Espere unos minutos.","Customize style":"Personalizar estilo","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike es operado por QHyralis, LLC, el destinatario del pago.","One payment. Permanent benefits.":"Un pago. Beneficios permanentes.","One-time payment":"Pago único","Permanent Supporter badge":"Insignia Supporter permanente","Ad-free on this account":"Sin publicidad en esta cuenta","Xsolla could not start checkout. Please try again.":"Xsolla no pudo iniciar el pago. Por favor inténtalo de nuevo.","Too many checkout attempts. Please wait a few minutes.":"Demasiados intentos de pago. Espere unos minutos.","Supporter checkout is not available yet.":"Supporter el proceso de pago aún no está disponible.","Supporter is already active on this account.":"Supporter ya está activo en esta cuenta.","Payment is being confirmed. Refresh in a moment.":"Se está confirmando el pago. Actualiza en un momento.","Supporter active":"Supporter activo","Your badge and ad-free access are permanently active.":"Su insignia y acceso sin publicidad están permanentemente activos.","Loading account...":"Cargando cuenta...","Sign in to continue":"Inicia sesión para continuar","Buy Now":"Comprar ahora","Payments are processed securely by Xsolla.":"Xsolla procesa los pagos de forma segura.","One purchase per account.":"Una compra por cuenta.","By continuing, you agree to the":"Al continuar, aceptas las",Terms:"Términos",and:"y","Privacy Policy":"PolÃtica de privacidad","Admin Benefits Active":"Beneficios de administrador activos","Contributor Benefits Active":"Beneficios para contribuyentes activos","Apply for Contributor":"Solicitar colaborador","Admin Active":"Administrador activo","Contributor Active":"Colaborador activo","Contributor Program":"Programa de contribuyentes","Creator Workshop":"Taller de creadores","Sign out":"Cerrar sesión",Community:"Comunidad","Main menu":"Menú principal","Single Player":"Un jugador",Multiplayer:"Multijugador",Mods:"Modificaciones",Settings:"Configuración","Install App":"Instalar aplicación","Community Contributor applications":"Solicitudes de colaborador de la comunidad",Dismiss:"Descartar","Dismiss contributor notice":"Descartar aviso de colaborador","Limited Alpha Event":"Evento Alfa limitado","Community Contributor applications are open":"Las solicitudes de colaborador de la comunidad están abiertas","Earn Ad-Free access and an exclusive multiplayer badge.":"Obtén acceso sin publicidad y una insignia multijugador exclusiva.","Ends Sep 8, 2026 at 11:59 PM PT":"Finaliza el 8 de septiembre de 2026 a las 11:59 p.m. PT","View details":"Ver detalles",Back:"Atrás",Menu:"Menú","New Run":"Nueva ejecución","SINGLE PLAYER":"UN JUGADOR",MULTIPLAYER:"MULTIJUGADOR","Preparing Stage":"Etapa de preparación","My Dub Premiere":"Mi estreno doblaje","Judge Score":"Puntuación del juez","Final Score":"Puntuación final","Dub Stage":"Etapa de doblaje","Create or Join":"Crear o unirse","Recent Dub":"Doblaje reciente","PLAYER OPTIONS":"OPCIONES DE JUGADOR","Press F11 for fullscreen.":"Presione F11 para pantalla completa.","MOD MANAGER":"ADMINISTRADOR DE MODIFICACIONES","Audio Settings":"Configuración de audio","Control game audio levels.":"Controla los niveles de audio del juego.","Master Volume":"Volumen maestro","Sound Effects":"Efectos de sonido","Background Music":"Música de fondo","Input Device":"Dispositivo de entrada","Voice Processing":"Procesamiento de voz","Microphone Volume":"Volumen del micrófono","Changes recording playback and exports. Scoring is unaffected.":"Cambia la reproducción, reproducción y exportación de grabaciones. La puntuación no se ve afectada.",Natural:"naturales","Browser noise reduction":"Reducción de ruido del navegador","Clean voice":"voz limpia","Best clarity for headphones and quiet rooms.":"La mejor claridad para auriculares y habitaciones silenciosas.","Uses only your browser's built-in noise reduction.":"Utiliza únicamente la reducción de ruido incorporada en su navegador.","No browser or Voxalike voice processing.":"Sin navegador ni procesamiento de voz Voxalike.","Raw microphone active":"Micrófono sin formato activo","Browser and Voxalike voice processing are disabled; no calibration is needed.":"El navegador y el procesamiento de voz Voxalike están deshabilitados; no se necesita calibración.","Record a short sample to hear your microphone without voice processing.":"Grabe una breve muestra para escuchar su micrófono sin procesamiento de voz.","Listen to the sample to hear your microphone without voice processing.":"Escuche la muestra para escuchar su micrófono sin procesamiento de voz.","Browser noise reduction active":"Reducción de ruido del navegador activa","Your browser handles noise reduction automatically; no calibration is needed.":"Su navegador maneja la reducción de ruido automáticamente; no se necesita calibración.","Record a short sample to hear your browser's built-in noise reduction.":"Grabe una breve muestra para escuchar la reducción de ruido incorporada en su navegador.","Listen to the sample to hear your browser's built-in noise reduction.":"Escuche la muestra para escuchar la reducción de ruido incorporada en su navegador.","Play
2sample":"Reproducir muestra","Stronger cleanup for fans, keyboards and noisy rooms.":"Limpieza más exhaustiva para ventiladores, teclados y salas ruidosas.",Default:"Predeterminado","Microphone {number}":"Micrófono {number}","Mic Check":"Comprobación de micrófono","Recording Sync":"Sincronización de grabación","Left: earlier. Right: later.":"Izquierda: antes. Derecha: más tarde.","Voice timing":"Sincronización de voz",Earlier:"Antes",Later:"Más tarde","Input Level":"Nivel de entrada","Stay Silent":"permanecer en silencio","Measuring background noise for 2 seconds.":"Medición del ruido de fondo durante 2 segundos.","Speak Normally":"hablar normalmente","Record your voice at normal volume for 3-5 seconds.":"Graba tu voz a volumen normal durante 3-5 segundos.","Stay silent for 2 seconds. Measuring background noise.":"Permanezca en silencio durante 2 segundos. Medición del ruido de fondo.","Speak at your normal volume for 3-5 seconds.":"Hable a su volumen normal durante 3-5 segundos.","This mic check is applied to future dubbing recordings.":"Esta verificación de micrófono se aplica a futuras grabaciones de doblaje.","Compare the samples, then apply this cleanup to future recordings.":"Compare las muestras y luego aplique esta limpieza a grabaciones futuras.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"La calibración de su micrófono guardada se aplica a futuras grabaciones de doblaje en este navegador.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"Permanezca en silencio durante 2 segundos y luego hable normalmente durante 3 a 5 segundos. El resultado se puede aplicar a la limpieza del ruido del juego.","Use Mic Check to connect":"Utilice Mic Check para conectarse","Requesting microphone":"Solicitando micrófono","Microphone Ready":"Micrófono listo","Permission denied":"Permiso denegado","Microphone Unavailable":"Micrófono no disponible","Microphone may be occupied":"El micrófono puede estar ocupado","Selected device unavailable":"El dispositivo seleccionado no está disponible","Device disconnected":"Dispositivo desconectado","Calibration saved · mic reconnects when needed":"Calibración guardada · El micrófono se vuelve a conectar cuando es necesario","Microphone could not start":"El micrófono no pudo iniciarse","Mic calibration applied":"Calibración de micrófono aplicada","Mic calibration not applied":"Calibración de micrófono no aplicada","Noise cleanup is active for future recordings.":"La limpieza de ruido está activa para futuras grabaciones.","Run Mic Check and apply it to improve noise cleanup.":"Ejecute Mic Check y aplÃquelo para mejorar la limpieza del ruido.",Applied:"Aplicado","Ready to apply":"Listo para aplicar","Noise:":"Ruido:","Volume:":"Volumen:","Peaks:":"Picos:",Raw:"crudo",Cleaned:"limpiado",Retake:"Retomar","Apply Mic Check":"Aplicar verificación de micrófono","Mic is too loud":"El micrófono suena demasiado alto.","Background noise is high":"El ruido de fondo es alto","Voice is quiet":"la voz es tranquila","Voice is loud":"la voz es fuerte","Mic sounds ready":"El micrófono suena listo","Start Mic Check":"Iniciar comprobación de micrófono","Connecting Mic...":"Conectando el micrófono...","Stop Recording":"Detener grabación",Record:"Registro","Retry Mic":"Reintentar micrófono",Replay:"Reproducir","My Take":"Mi opinión",Retry:"Reintentar",Next:"Siguiente",Finish:"terminar",Skip:"Saltar","GET READY":"PREPÃRATE",RECORDING:"GRABACIÃN","CLIP {current} / {total}":"CLIP {current} / {total}","Jump ahead to another clip":"Saltar a otro clip","Replay original clip":"Reproducir clip original","Finish pack":"Paquete final","Go to next clip":"Ir al siguiente vÃdeo",Start:"Empezar","Starting...":"Empezando...","Could not start this mod":"No se pudo iniciar este mod","Copy start error details":"Copiar detalles del error de inicio","Creator:":"Creador:","Characters:":"Personajes:","No Dub Mods Yet":"Aún no hay modificaciones de doblaje","Subscribe to a pack from the Workshop, then start your game.":"SuscrÃbete a un paquete del Taller y luego comienza tu juego.","Browse Workshop":"Explorar taller","My Mods":"Mis modificaciones","Refresh Mods":"Actualizar modificaciones","Refreshing Mods...":"Modificaciones refrescantes...","Refreshing...":"Refrescante...","Mods are up to date.":"Las modificaciones están actualizadas.","{count} mods":"{count} modificaciones","{count} / syncing Workshop":"{count} / Taller de sincronización","{count} / {name} judges in use":"{count} / {name} jueces en uso","{count} / default judges in use":"{count} / jueces predeterminados en uso","Local mod import options":"Opciones de importación de mods locales","Import local file":"Importar archivo local","Dub Mod ZIP":"Mod de doblaje ZIP","Dub Mod Folder":"Carpeta de modificación de copia","Judge Mod ZIP":"Mod de juez ZIP","Search by mod, creator, character, or type":"Bu
2scar por mod, creador, personaje o tipo","Search mods":"Modificaciones de búsqueda","Clear search":"Borrar búsqueda","Syncing Workshop subscriptions":"Sincronización de suscripciones al taller","Checking which installed mods belong to your account.":"Comprobando qué mods instalados pertenecen a su cuenta.","No mods yet":"Aún no hay modificaciones","Subscribe to a pack from the Workshop, then start a game in":"SuscrÃbete a un paquete del Taller y luego comienza un juego en",Solo:"en solitario",or:"o"," game.":"juego.",'No mods match "{query}"':'Ninguna modificación coincide con "{query}"',"Clear Search":"Borrar búsqueda","View installation progress in Mods":"Ver el progreso de la instalación en Mods","View in Mods":"Ver en modificaciones","Show clip scores":"Mostrar partituras de clips","Reconnect microphone and record":"Vuelva a conectar el micrófono y grabe","Play your recorded take with background audio":"Reproduzca su toma grabada con audio de fondo","Play your recorded take":"Reproduce tu toma grabada","Background volume":"Volumen de fondo",Background:"Antecedentes",Cancel:"Cancelar","Report Player":"Denunciar jugador","Mod feedback":"Comentarios del mod","Report this mod":"Reportar este mod","Report reason":"Motivo del informe","Incorrect role information":"Información de rol incorrecta","Incorrect language":"Idioma incorrecto","Broken or misleading mod":"Mod roto o engañoso","Inappropriate content":"Contenido inapropiado","Copyright or rights issue":"Problema de derechos de autor o derechos","Spam or abuse":"Spam o abuso","Describe the problem":"Describe el problema","Send Report":"Enviar informe","Sending...":"Enviando...","Report received.":"Informe recibido.","Report could not be submitted.":"No se pudo enviar el informe.","Close report dialog":"Cerrar cuadro de diálogo de informe","Reported Player":"Jugador reportado","Report Type":"Tipo de informe","Choose a violation type":"Elija un tipo de infracción",Description:"Descripción","Describe what happened so moderators can review the report.":"Describe lo que sucedió para que los moderadores puedan revisar el informe.","At least 10 characters.":"Al menos 10 caracteres.","Submit Report":"Enviar informe","Harassment / Abuse":"Acoso / Abuso","Hate Speech":"discurso de odio",Spam:"spam","Inappropriate Content":"Contenido inapropiado",Other:"Otro","Clip Scores":"Partituras de clips","{completed} / {total} clips completed":"{completed} / {total} clips completados",Detail:"Detalle","Back to final score":"Volver a la puntuación final","No playable dub video is available.":"No hay ningún vÃdeo doblado reproducible disponible.","Play my dubbed work":"Reproduce mi trabajo doblado","PUBLIC MULTIPLAYER":"MULTIJUGADOR PÃBLICO","Public Lobby":"VestÃbulo público","Refresh public rooms":"Actualizar las salas públicas","Multiplayer Lobby":"Sala multijugador","Refresh rooms":"Actualizar habitaciones","All Languages":"Todos los idiomas",Status:"Estado","All Rooms":"Todas las habitaciones","Waiting Only":"Sólo esperando","In Progress":"En progreso",Results:"Resultados",Waiting:"esperando","No public rooms match these filters.":"Ninguna sala pública coincide con estos filtros.","Loading public rooms...":"Cargando salas públicas...","No rooms match these filters.":"Ninguna habitación coincide con estos filtros.","Loading rooms...":"Cargando habitaciones...","Room visibility":"Visibilidad de la habitación",Private:"Privado","Private Room":"Habitación Privada",Public:"Público","Room Language":"Idioma de la habitación","Sign in to create a public room":"Inicia sesión para crear una sala pública","Waiting for host to choose a mod":"Esperando a que el anfitrión elija un mod",Join:"Unirse","View Profile":"Ver perfil","Host must start: {count}s":"El host debe iniciar: {count}s","Room Chat":"Chat de sala","No messages yet.":"Aún no hay mensajes.","Message the room":"EnvÃa un mensaje a la habitación","Reconnecting chat...":"Reconectando el chat...","Send message":"enviar mensaje","Open room chat":"Sala de chat abierta","Close room chat":"Cerrar sala de chat","Players online":"Jugadores en lÃnea","{count} unread messages":"{count} mensajes no leÃdos","{name} disconnected.":"{name} desconectado.","{name} reconnected.":"{name} reconectado.","{name} left the room.":"{name} salió de la habitación.","This public room has been waiting for 50 minutes.":"Esta sala pública lleva 50 minutos esperando.","Player Name":"Nombre del jugador","Create Room":"Crear habitación","Creating...":"Creando...","Room Code":"Código de habitación","Join Room":"Unirse a la sala","Joining...":"Unirse...","Leave Room":"salir de la habitación","ROOM CODE":"CÃDIGO DE HABITACIÃN","Copy Code":"Copiar código","Copy Invite Link":"Copiar enlace de invitación",Copied:"Copiado",Host:"Anfitrión",Ready:"Listo",Rendering:"Representación","Not rendered":"No renderizado","Waiting â click to switch rendering":"Esperando: haga clic para cambiar de renderizado","N
2ot Ready":"No listo","Kick Player":"Jugador de patada","Host actions for {name}":"Acciones del anfitrión para {name}","Empty player slot":"Ranura de jugador vacÃa","Ready Up":"Listo","Cancel Ready":"Cancelar Listo","Installing room mod":"Instalando mod de habitación","Installing room mod {percent}%":"Instalando mod de habitación {percent}%","Start Game":"Iniciar juego","Starting in {count}":"Comenzando en {count}","How multiplayer mods sync":"Cómo se sincronizan los mods multijugador","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"Las suscripciones al taller se instalan automáticamente para los jugadores que hayan iniciado sesión. Todos los presentes en la sala deben descargar e importar las modificaciones locales.","Change Mod":"Cambiar modo","Choose a Mod":"Elige un mod","Import a mod first":"Importa un mod primero","Multiplayer mode":"Modo multijugador","Co-op":"cooperativa","Rotate turns or let players claim open clips.":"Rota turnos o deja que los jugadores reclamen clips abiertos.","Everyone records the same clips and compares scores.":"Todos graban los mismos clips y comparan puntuaciones.","Co-op Turn Mode":"Modo de turno cooperativo","Co-op Assignment":"Asignación cooperativa","Rotate turns, claim clips, or claim roles.":"Rota turnos, reclama clips o reclama roles.","Round Robin":"Ronda Robin","Claim Mode":"Modo de reclamación","Role Claim":"Reclamación de rol","Co-op · Role Claim":"Cooperativo · Reclamación de roles","Choose Roles":"Elija roles","Back to room":"volver a la habitación","All roles are assigned.":"Todos los roles están asignados.","Start Dubbing":"Empezar a doblar","Waiting for the host to start dubbing.":"Esperando a que el presentador comience a doblar.","Syncing role selection":"Sincronización de selección de roles","This mod has no roles to claim":"Este mod no tiene roles que reclamar.","Claim every role before starting":"Reclama todos los roles antes de comenzar","Choose your roles":"Elige tus roles","Choose your roles.":"Elige tus roles.",Claim:"Reclamar",Release:"Lanzamiento","Returned to lobby.":"Regresó al vestÃbulo.","Could not start role selection.":"No se pudo iniciar la selección de roles.","Could not return to lobby.":"No se pudo regresar al lobby.","Could not update role.":"No se pudo actualizar el rol.","Session complete":"Sesión completa","Click a role to claim it. Click again to release it.":"Haga clic en un rol para reclamarlo. Haga clic nuevamente para liberarlo.","Preview lines":"LÃneas de vista previa","Loading lines...":"Cargando lÃneas...","LINE PREVIEW":"VISTA PREVIA DE LÃNEA","{count} lines":"{count} lÃneas","Filter lines by role":"Filtrar lÃneas por rol",All:"Todos",Unassigned:"No asignado","No lines found for this role.":"No se encontraron lÃneas para este rol.",claimed:"reclamado","Claim a role before the host starts. You can take more than one.":"Reclama un rol antes de que comience el anfitrión. Puedes tomar más de uno.","Randomize Player Order":"Orden aleatorio de los jugadores","Shuffles the player order once when the game starts, then continues rotating fairly.":"Mezcla el orden de los jugadores una vez cuando comienza el juego y luego continúa rotando de manera justa.","PVP Format":"Formato JcJ","Turn Battle":"Turno de batalla","Parallel Race":"Carrera paralela","Players record separately. Faster players get a higher score.":"Los jugadores graban por separado. Los jugadores más rápidos obtienen una puntuación más alta.","Players take turns recording the same clip before moving to the next one.":"Los jugadores se turnan para grabar el mismo clip antes de pasar al siguiente.","Allow Re-records":"Permitir regrabaciones","Player Limit":"LÃmite de jugadores",Confirm:"Confirmar","On: players can clear their current multiplayer take and record it again before submitting.":"Activado: los jugadores pueden borrar su toma multijugador actual y grabarla nuevamente antes de enviarla.","DUB MOD":"MODO DOBLAR","{count} players in room":"{count} jugadores en la sala","Score Judging":"Puntuación de evaluación","Judge feedback":"Comentarios del juez","Judge score stage":"Etapa de puntuación del juez","Preparing score":"Preparando partitura","Your {label} is":"Tu {label} es","Final score {score}":"Puntuación final {score}
2",pending:"pendiente",Continue:"Continuar",Duration:"Duración",Timing:"Sincronización",Energy:"EnergÃa",Pitch:"Tono","Perfect Fit":"Ajuste perfecto","Good Fit":"Buen ajuste",Uneven:"desigual","Too Short/Long":"Demasiado corto/largo","Locked In":"encerrado","On Time":"A tiempo",Close:"Cerrar","Off Beat":"fuera de ritmo","Stage Ready":"Listo para el escenario",Expressive:"expresivo",Solid:"Sólido",Flat:"Piso",Uncanny:"asombroso","Very Close":"Muy cerca","Close Enough":"Lo suficientemente cerca",Different:"Diferente","Perfect Dub":"Doblaje perfecto","Viral Performance":"Rendimiento viral","Scene Stealer":"Ladrón de escenas","Great Take":"Gran toma","Solid Dub":"Doblaje sólido","Getting There":"Llegar allÃ","Keep Practicing":"Sigue practicando","Download exports":"Descargar exportaciones","Download My Work":"Descargar Mi Trabajo","Your browser needs one more tap to play this mod video.":"Tu navegador necesita un toque más para reproducir este vÃdeo mod.","Continue Export":"Continuar exportando","Video subtitle option":"Opción de subtÃtulos de vÃdeo",Clean:"limpio",Subtitles:"SubtÃtulos","Download my vertical Shorts video":"Descarga mi vÃdeo de Shorts verticales","Save Shorts":"Guardar pantalones cortos","My Shorts":"mis pantalones cortos",Video:"VÃdeo","Download my landscape video":"Descarga mi vÃdeo de paisaje.","Save Landscape":"Guardar paisaje","My Landscape":"mi paisaje","Download my audio track":"Descargar mi pista de audio","Save Audio":"Guardar audio","My Audio":"mi audio",Track:"Seguimiento","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Mantenga esta página abierta mientras se prepara. Los vÃdeos largos pueden durar varios minutos. Cuando esté listo, toque Guardar para usar el menú de guardar del iPhone.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"Las exportaciones son WebM. Se recomienda Chrome para la reproducción; Si necesita MP4, convierta el archivo usted mismo.","Share exports":"Compartir exportaciones",Share:"Compartir","Copy share caption":"Copiar tÃtulo compartido","Copy caption":"Copiar tÃtulo","Rotate your phone":"Gira tu teléfono","Landscape gives the video and recording controls more room.":"El paisaje le da más espacio a los controles de vÃdeo y grabación.","Stay in portrait":"Quédate en retrato","Previous run ended unexpectedly":"La ejecución anterior finalizó inesperadamente","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike recargado mientras un juego estaba activo. Copie este informe si la interrupción vuelve a ocurrir.","Copy crash report":"Copiar informe de fallos","Dismiss crash report":"Descartar informe de fallo","Page interrupted":"Página interrumpida","Your room and locally saved recordings can be restored after reloading.":"Su habitación y las grabaciones guardadas localmente se pueden restaurar después de recargarlas.","Reload Voxalike":"Recargar Voxalike","ROOM CLOSING SOON":"CIERRE DE SALA PRONTO","This room has been waiting for 10 minutes.":"Esta sala ha estado esperando durante 10 minutos.","Start the game within the next 10 minutes or the room will close automatically.":"Inicie el juego dentro de los próximos 10 minutos o la sala se cerrará automáticamente.","Room closed because no game started within 20 minutes.":"Sala cerrada porque no comenzó ningún juego en 20 minutos.","Room closed because the host did not reconnect within 5 minutes.":"La habitación se cerró porque el anfitrión no se volvió a conectar en 5 minutos.","Wait for disconnected players to reconnect":"Espere a que los jugadores desconectados se vuelvan a conectar",Reconnecting:"Reconectando","Game server updated":"Servidor de juegos actualizado","Connection interrupted":"Conexión interrumpida","Syncing the latest game server and restoring your room.":"Sincronizando el último servidor de juegos y restaurando tu sala.","Your connection dropped. Trying to reconnect to your room.":"Tu conexión se cortó. Intentando volver a conectarme con tu habitación."},pt:{"Interface Language":"Idioma da interface","Choose the language used by the game interface.":"Escolha o idioma usado pela interface do jogo.",Language:"Idioma",PLAY:"JOGAR",NEWS:"NOTÃCIAS","DEVELOPER NEWS":"NOTÃCIAS DO DESENVOLVEDOR","Voxalike News":"Voxalike NotÃcias",GUIDE:"GUIA",WORKSHOP:"OFICINA",SUPPORTER:"APOIADOR",SUPPORT:"SUPORTE","Sign in":"Faça login","Account menu":"Menu da conta",Notifications:"Notificações",Profile:"Perfil","Account details":"Detalhes da conta","Edit Profile":"Editar perfil",Studio:"Estúdio","Support Again":"Suporte novamente","Become a Supporter":"Torne-se um Supporter","Support Voxalike":"Suporte Voxalike","Voxalike Player":"
2Voxalike Jogador","Supporter profile preview":"Supporter visualização do perfil","Profile preview":"Visualização do perfil","6 name colors":"6 cores de nomes","3 avatar frames":"3 molduras de avatar","4 avatar frames":"4 molduras de avatar","3 avatar frames + 1 animated bonus frame":"3 molduras de avatar + 1 moldura animada bônus","Try on {frame}":"Experimentar {frame}","Supporter purchase":"Supporter compra","one time":"uma vez","No subscription":"Sem assinatura","Animated Supporter badge":"Selo Supporter animado","Checkout could not start. Please try again.":"A finalização da compra não pôde ser iniciada. Por favor, tente novamente.","Checkout was canceled. Nothing was charged.":"O check-out foi cancelado. Nada foi cobrado.","Payment is being confirmed. Your benefits will appear here automatically.":"O pagamento está sendo confirmado. Seus benefÃcios aparecerão aqui automaticamente.","Buy Supporter for US$5":"Compre Supporter por US$ 5","Gift code checkout is not available yet.":"A finalização da compra do código presente ainda não está disponÃvel.","Payment is being confirmed. Your gift code will appear here automatically.":"O pagamento está sendo confirmado. Seu código de presente aparecerá aqui automaticamente.","Buy Supporter or a gift code":"Compre Supporter ou um código de presente","Activate on this account":"Ativar nesta conta","Supporter benefits start after payment.":"Os benefÃcios Supporter começam após o pagamento.","Buy a Supporter gift code":"Compre um código de presente Supporter","Give Supporter":"Dê Supporter","Manage gift codes":"Gerenciar códigos de presente","SUPPORTER GIFT":"PRESENTE DE APOIADOR","Gift delivery method":"Método de entrega de presentes","Send to a player":"Enviar para um jogador","Delivered directly to their account":"Entregue diretamente em sua conta","Buy gift codes":"Compre códigos de presente","Share the codes yourself":"Compartilhe você mesmo os códigos","Player's Voxalike email":"E-mail do jogador Voxalike","Searching...":"Procurando...","Find player":"Encontrar jogador","Could not find that player.":"Não foi possÃvel encontrar esse jogador.","Confirm recipient":"Confirmar destinatário","Open profile":"Abrir perfil","This is your account":"Esta é a sua conta","Already has Supporter":"Já tem Supporter","Gift Supporter for US$5":"Presente Supporter por US$ 5","Number of gift codes":"Número de códigos de presente",Total:"Total","Buy {count} gift codes":"Compre {count} códigos de presente","Buy 1 gift code":"Compre 1 código de presente","SUPPORTER GIFTS":"PRESENTES DE APOIADORES","{count} unused codes":"{count} códigos não utilizados","1 unused code":"1 código não utilizado","Copy all unused":"Copie todos os não utilizados","Download unused":"Download não utilizado","Loading gift codes...":"Carregando códigos de presente...","You do not have any gift codes yet.":"Você ainda não tem nenhum código de presente.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"O pagamento está sendo confirmado. Seus códigos de presente aparecerão automaticamente no gerenciador de presentes.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"O pagamento está sendo confirmado. O presente será entregue em {name} automaticamente.","Supporter was delivered to {name}.":"Supporter foi entregue a {name}.","the selected player":"o jogador selecionado","That player already has Supporter.":"Esse jogador já tem Supporter.","Use Activate on this account to buy Supporter for yourself.":"Use Ativar nesta conta para comprar Supporter para você.","That Voxalike account is no longer available.":"Essa conta Voxalike não está mais disponÃvel.","Give the code to any player to redeem.":"Dê o código a qualquer jogador para resgatar.","Your gift codes":"Seus códigos de presente",Redeemed:"Resgatado","Ready to redeem":"Pronto para resgatar","Copy code":"Copiar código","Redeem Supporter code":"Resgatar código Supporter","Enter Supporter code":"Insira o código Supporter","Redeem code":"Resgatar código","Code redeemed. Supporter is now active.":"Código resgatado. Supporter agora está ativo.","This code is invali
2d or is not ready yet.":"Este código é inválido ou ainda não está pronto.","This code has already been redeemed.":"Este código já foi resgatado.","Too many redemption attempts. Please wait a few minutes.":"Muitas tentativas de resgate. Aguarde alguns minutos.","Customize style":"Personalizar estilo","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike é operado pela QHyralis, LLC, o destinatário do pagamento.","One payment. Permanent benefits.":"Um pagamento. BenefÃcios permanentes.","One-time payment":"Pagamento único","Permanent Supporter badge":"Selo Supporter permanente","Ad-free on this account":"Sem anúncios nesta conta","Xsolla could not start checkout. Please try again.":"Xsolla não pôde iniciar a finalização da compra. Por favor, tente novamente.","Too many checkout attempts. Please wait a few minutes.":"Muitas tentativas de checkout. Aguarde alguns minutos.","Supporter checkout is not available yet.":"A finalização da compra Supporter ainda não está disponÃvel.","Supporter is already active on this account.":"Supporter já está ativo nesta conta.","Payment is being confirmed. Refresh in a moment.":"O pagamento está sendo confirmado. Atualize em um momento.","Supporter active":"Supporter ativo","Your badge and ad-free access are permanently active.":"Seu selo e acesso sem anúncios estão permanentemente ativos.","Loading account...":"Carregando conta...","Sign in to continue":"Faça login para continuar","Buy Now":"Compre agora","Payments are processed securely by Xsolla.":"Os pagamentos são processados com segurança pela Xsolla.","One purchase per account.":"Uma compra por conta.","By continuing, you agree to the":"Ao continuar, você concorda com os",Terms:"Termos",and:"e","Privacy Policy":"PolÃtica de Privacidade","Admin Benefits Active":"BenefÃcios administrativos ativos","Contributor Benefits Active":"BenefÃcios do Contribuidor Ativos","Apply for Contributor":"Inscreva-se para Colaborador","Admin Active":"Administrador ativo","Contributor Active":"Colaborador ativo","Contributor Program":"Programa Contribuidor","Creator Workshop":"Oficina de criadores","Sign out":"Sair",Community:"Comunidade","Main menu":"Menu principal","Single Player":"Ãnico jogador",Multiplayer:"Multijogador",Mods:"Modificações",Settings:"Configurações","Install App":"Instalar aplicativo","Community Contributor applications":"Inscrições para Contribuidores da Comunidade",Dismiss:"Dispensar","Dismiss contributor notice":"Dispensar aviso de colaborador","Limited Alpha Event":"Evento Alfa Limitado","Community Contributor applications are open":"As inscrições para Contribuidores da Comunidade estão abertas","Earn Ad-Free access and an exclusive multiplayer badge.":"Ganhe acesso sem anúncios e um emblema multijogador exclusivo.","Ends Sep 8, 2026 at 11:59 PM PT":"Termina em 8 de setembro de 2026 à s 23h59 (horário do PacÃfico)","View details":"Ver detalhes",Back:"Voltar",Menu:"Cardápio","New Run":"Nova corrida","SINGLE PLAYER":"JOGADOR ÃNICO",MULTIPLAYER:"MULTIJOGADOR","Preparing Stage":"Estágio de Preparação","My Dub Premiere":"Minha estreia dublada","Judge Score":"Pontuação do Juiz","Final Score":"Pontuação Final","Dub Stage":"Estágio de dublagem","Create or Join":"Crie ou participe","Recent Dub":"Dublagem recente","PLAYER OPTIONS":"OPÃÃES DE JOGADOR","Press F11 for fullscreen.":"Pressione F11 para tela cheia.","MOD MANAGER":"GERENCIADOR DE MODOS","Audio Settings":"Configurações de áudio","Control game audio levels.":"Controle os nÃveis de áudio do jogo.","Master Volume":"Volume Mestre","Sound Effects":"Efeitos Sonoros","Background Music":"Música de fundo","Input Device":"Dispositivo de entrada","Voice Processing":"Processamento de voz","Microphone Volume":"Volume do microfone","Changes recording playback and exports. Scoring is unaffected.":"Altera a reprodução e exportação da gravação. A pontuação não é afetada.",Natural:"Naturais","Browser noise reduction":"Redução de ruÃdo do navegador","Clean voice":"Voz limpa","Best clarity for headphones and quiet rooms.":"Melhor clareza para fones de ouvido e salas silenciosas.","Uses only your browser's built-in noise reduction.":"Usa apenas a redução de ruÃdo integrada do seu navegador.","No browser or Voxalike voice processing.":"Sem navegador ou processamento de voz Voxalike.","Raw microphone active":"Microfone bruto ativo","Browser and Voxalike voice processing are disabled; no calibration is needed.":"O navegador e o processamento de voz Voxalike estão desativados; nenhuma calibração é necessária.","Record a short sample to hear your microphone without voice processing.":"Grave uma pequena amostra para ouvir seu microfone sem processamento de voz.","Listen to the sample to hear your microphone without voice processing.":"Ouça a amostra para ouvir seu microfone sem processamento de voz.","Browser noise reduction active":"Redução de ruÃdo do navegador ativa","Your browser handles noise reduction automatically; no calibration is needed.":"Seu navegador lida com a redução de ruÃdo automaticamente; nenhuma calibração é necessária.","Record a short sample to hear your browser's built-in noise reduction.":"Grave uma pequena amostra para ouvir a redução de ruÃdo integrada do seu navegador.","Listen to the sample to hear your browser's built-in noise reduction.":"Ouça a amostra para ouvir a redução de ruÃdo integrada do seu navegador.","Play
2sample":"Amostra de reprodução","Stronger cleanup for fans, keyboards and noisy rooms.":"Limpeza mais forte para ventiladores, teclados e salas barulhentas.",Default:"Padrão","Microphone {number}":"Microfone {number}","Mic Check":"Verificação do microfone","Recording Sync":"Sincronização de Gravação","Left: earlier. Right: later.":"Esquerda: antes. Certo: mais tarde.","Voice timing":"Tempo de voz",Earlier:"Anteriormente",Later:"Mais tarde","Input Level":"NÃvel de entrada","Stay Silent":"Fique em silêncio","Measuring background noise for 2 seconds.":"Medindo o ruÃdo de fundo por 2 segundos.","Speak Normally":"Fale normalmente","Record your voice at normal volume for 3-5 seconds.":"Grave sua voz no volume normal por 3-5 segundos.","Stay silent for 2 seconds. Measuring background noise.":"Fique em silêncio por 2 segundos. Medindo o ruÃdo de fundo.","Speak at your normal volume for 3-5 seconds.":"Fale no volume normal por 3 a 5 segundos.","This mic check is applied to future dubbing recordings.":"Esta verificação de microfone é aplicada a futuras gravações de dublagem.","Compare the samples, then apply this cleanup to future recordings.":"Compare as amostras e aplique essa limpeza em gravações futuras.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"A calibração do microfone salva será aplicada a futuras gravações de dublagem neste navegador.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"Fique em silêncio por 2 segundos e depois fale normalmente por 3 a 5 segundos. O resultado pode ser aplicado à limpeza de ruÃdo do jogo.","Use Mic Check to connect":"Use Mic Check para conectar","Requesting microphone":"Solicitando microfone","Microphone Ready":"Microfone pronto","Permission denied":"Permissão negada","Microphone Unavailable":"Microfone indisponÃvel","Microphone may be occupied":"O microfone pode estar ocupado","Selected device unavailable":"Dispositivo selecionado indisponÃvel","Device disconnected":"Dispositivo desconectado","Calibration saved · mic reconnects when needed":"Calibração salva · microfone reconecta quando necessário","Microphone could not start":"O microfone não pôde ser iniciado","Mic calibration applied":"Calibração do microfone aplicada","Mic calibration not applied":"Calibração do microfone não aplicada","Noise cleanup is active for future recordings.":"A limpeza de ruÃdo está ativa para gravações futuras.","Run Mic Check and apply it to improve noise cleanup.":"Execute Mic Check e aplique-o para melhorar a limpeza de ruÃdo.",Applied:"Aplicado","Ready to apply":"Pronto para aplicar","Noise:":"RuÃdo:","Volume:":"Volume:","Peaks:":"Picos:",Raw:"Cru",Cleaned:"Limpo",Retake:"Retomar","Apply Mic Check":"Aplicar verificação de microfone","Mic is too loud":"O microfone está muito alto","Background noise is high":"O ruÃdo de fundo é alto","Voice is quiet":"A voz está quieta","Voice is loud":"A voz está alta","Mic sounds ready":"O microfone parece pronto","Start Mic Check":"Iniciar verificação do microfone","Connecting Mic...":"Conectando microfone...","Stop Recording":"Pare de gravar",Record:"Gravar","Retry Mic":"Tentar novamente o microfone",Replay:"Repetir","My Take":"Minha opinião",Retry:"Tentar novamente",Next:"Próximo",Finish:"Concluir",Skip:"Pular","GET READY":"PREPARE-SE",RECORDING:"GRAVAÃÃO","CLIP {current} / {total}":"CLIPE {current} / {total}","Jump ahead to another clip":"Avance para outro clipe","Replay original clip":"Reproduzir o clipe original","Finish pack":"Concluir pacote","Go to next clip":"Ir para o próximo clipe",Start:"Começar","Starting...":"Começando...","Could not start this mod":"Não foi possÃvel iniciar este mod","Copy start error details":"Copiar detalhes do erro de inÃcio","Creator:":"Criador:","Characters:":"Personagens:","No Dub Mods Yet":"Ainda não há mods de dublagem","Subscribe to a pack from the Workshop, then start your game.":"Assine um pacote do Workshop e comece o jogo.","Browse Workshop":"Navegar na oficina","My Mods":"Meus mods","Refresh Mods":"Atualizar mods","Refreshing Mods...":"Atualizando mods...","Refreshing...":"Refrescante...","Mods are up to date.":"Os mods estão atualizados.","{count} mods":"{count} modificações","{count} / syncing Workshop":"{count} / oficina de sincronização","{count} / {name} judges in use":"{count} / {name} juÃzes em uso","{count} / default judges in use":"{count} / juÃzes padrão em uso","Local mod import options":"Opções de importação de mod local","Import local file":"Importar arquivo local","Dub Mod ZIP":"Mod de dublagem ZIP","Dub Mod Folder":"Pasta Mod Dub","Judge Mod ZIP":"Juiz Mod ZIP","Search by mod, creator, character, or type":"Pesquise por mod, criador, personagem ou tipo","Search mods":"Pesquisar mods","Clear search":"Limpar pesquisa","Syncing Workshop subscriptions":"Sincronizando assinaturas do Workshop","Checking which installed mods belong to your account.":"Verificando quais mods instalados pertencem à sua conta.","No mods yet":"Ainda não há mods","Subscribe to a pack from the Workshop, then start a game in":"Inscreva-se em um pacote do Workshop e inicie um jogo em",Solo:"Sozinho",or:"ou"," game.":"jogo.",'No mods match "{query}"':'Nenhum mod corresponde a "{query}"',"Clear Search":"Limpar pesquisa","View installation progress in Mods":"Veja o progresso da instalação em Mods","Vie
2w in Mods":"Ver em mods","Show clip scores":"Mostrar pontuações de clipes","Reconnect microphone and record":"Reconecte o microfone e grave","Play your recorded take with background audio":"Reproduza sua gravação com áudio de fundo","Play your recorded take":"Reproduza sua tomada gravada","Background volume":"Volume de fundo",Background:"Plano de fundo",Cancel:"Cancelar","Report Player":"Denunciar jogador","Mod feedback":"Feedback do mod","Report this mod":"Denunciar este mod","Report reason":"Motivo do relatório","Incorrect role information":"Informações de função incorretas","Incorrect language":"Idioma incorreto","Broken or misleading mod":"Mod quebrado ou enganoso","Inappropriate content":"Conteúdo impróprio","Copyright or rights issue":"Questão de direitos autorais ou direitos","Spam or abuse":"Spam ou abuso","Describe the problem":"Descreva o problema","Send Report":"Enviar relatório","Sending...":"Enviando...","Report received.":"Relatório recebido.","Report could not be submitted.":"Não foi possÃvel enviar o relatório.","Close report dialog":"Fechar caixa de diálogo do relatório","Reported Player":"Jogador denunciado","Report Type":"Tipo de relatório","Choose a violation type":"Escolha um tipo de violação",Description:"Descrição","Describe what happened so moderators can review the report.":"Descreva o que aconteceu para que os moderadores possam revisar o relatório.","At least 10 characters.":"Pelo menos 10 caracteres.","Submit Report":"Enviar relatório","Harassment / Abuse":"Assédio / Abuso","Hate Speech":"Discurso de ódio",Spam:"Spam","Inappropriate Content":"Conteúdo impróprio",Other:"Outro","Clip Scores":"Pontuações de clipes","{completed} / {total} clips completed":"{completed} / {total} clipes concluÃdos",Detail:"Detalhe","Back to final score":"De volta à pontuação final","No playable dub video is available.":"Nenhum vÃdeo dublado reproduzÃvel está disponÃvel.","Play my dubbed work":"Reproduzir meu trabalho dublado","PUBLIC MULTIPLAYER":"MULTIJOGADOR PÃBLICO","Public Lobby":"Lobby Público","Refresh public rooms":"Atualizar salas públicas","Multiplayer Lobby":"Lobby multijogador","Refresh rooms":"Atualizar salas","All Languages":"Todos os idiomas",Status:"Estado","All Rooms":"Todos os quartos","Waiting Only":"Esperando apenas","In Progress":"Em andamento",Results:"Resultados",Waiting:"Esperando","No public rooms match these filters.":"Nenhuma sala pública corresponde a esses filtros.","Loading public rooms...":"Carregando salas públicas...","No rooms match these filters.":"Nenhuma sala corresponde a esses filtros.","Loading rooms...":"Carregando salas...","Room visibility":"Visibilidade da sala",Private:"Privado","Private Room":"Quarto Privado",Public:"Público","Room Language":"Idioma da sala","Sign in to create a public room":"Faça login para criar uma sala pública","Waiting for host to choose a mod":"Esperando que o host escolha um mod",Join:"Junte-se","View Profile":"Ver perfil","Host must start: {count}s":"O host deve iniciar: {count}s","Room Chat":"Sala de bate-papo","No messages yet.":"Nenhuma mensagem ainda.","Message the room":"Envie uma mensagem para a sala","Reconnecting chat...":"Reconectando bate-papo...","Send message":"Enviar mensagem","Open room chat":"Bate-papo em sala aberta","Close room chat":"Fechar bate-papo da sala","Players online":"Jogadores on-line","{count} unread messages":"{count} mensagens não lidas","{name} disconnected.":"{name} desconectado.","{name} reconnected.":"{name} reconectado.","{name} left the room.":"{name} saiu da sala.","This public room has been waiting for 50 minutes.":"Esta sala pública está esperando há 50 minutos.","Player Name":"Nome do jogador","Create Room":"Criar sala","Creating...":"Criando...","Room Code":"Código do quarto","Join Room":"Entrar na sala","Joining...":"Juntando-se...","Leave Room":"Sair da sala","ROOM CODE":"CÃDIGO DO QUARTO","Copy Code":"Copiar código","Copy Invite Link":"Copiar link de convite",Copied:"Copiado",Host:"Anfitrião",Ready:"Pronto",Rendering:"Renderização","Not rendered":"Não renderizado","Waiting â click to switch rendering":"Aguardando â clique para alternar a renderização","Not Ready":"Não está pronto","Kick Player":"Chutar Jogador","Host actions for {name}":"Ações de host para {name}","Empty player slot":"Slot de jogador vazio","Ready Up":"Pronto","Cancel Ready":"Cancelar pronto","Installing room mod":"Instalando mod de sala","Installing room mod {percent}%":"Instalando mod de sala {percent}%","Start Game":"Iniciar jogo","Starting in {count}":"Começando em {count}","How multiplayer mods sync":"Como os mods multijogador são sincronizados","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"As assinaturas do Workshop são instaladas automaticamente para jogadores conectados. Os mods locais devem ser baixados e importados por todos na sala.","Change Mod":"Alterar modo","Choose a Mod":"Escolha um mod","Import a mod f
2irst":"Importe um mod primeiro","Multiplayer mode":"Modo multijogador","Co-op":"Cooperativa","Rotate turns or let players claim open clips.":"Gire os turnos ou deixe os jogadores reivindicarem clipes abertos.","Everyone records the same clips and compares scores.":"Todos gravam os mesmos clipes e comparam as pontuações.","Co-op Turn Mode":"Modo de turno cooperativo","Co-op Assignment":"Atribuição Cooperativa","Rotate turns, claim clips, or claim roles.":"Gire turnos, reivindique clipes ou reivindique funções.","Round Robin":"Rodada Robin","Claim Mode":"Modo de reivindicação","Role Claim":"Reivindicação de função","Co-op · Role Claim":"Cooperativa · Reivindicação de função","Choose Roles":"Escolha funções","Back to room":"De volta ao quarto","All roles are assigned.":"Todas as funções são atribuÃdas.","Start Dubbing":"Comece a dublar","Waiting for the host to start dubbing.":"Esperando o host começar a dublar.","Syncing role selection":"Sincronizando seleção de função","This mod has no roles to claim":"Este mod não tem funções a serem reivindicadas","Claim every role before starting":"Reivindique todas as funções antes de começar","Choose your roles":"Escolha suas funções","Choose your roles.":"Escolha seus papéis.",Claim:"Reivindicar",Release:"Liberar","Returned to lobby.":"Retornou ao lobby.","Could not start role selection.":"Não foi possÃvel iniciar a seleção de função.","Could not return to lobby.":"Não foi possÃvel retornar ao lobby.","Could not update role.":"Não foi possÃvel atualizar a função.","Session complete":"Sessão concluÃda","Click a role to claim it. Click again to release it.":"Clique em uma função para reivindicá-la. Clique novamente para liberá-lo.","Preview lines":"Visualizar linhas","Loading lines...":"Carregando linhas...","LINE PREVIEW":"VISUALIZAÃÃO DE LINHA","{count} lines":"{count} linhas","Filter lines by role":"Filtrar linhas por função",All:"Todos",Unassigned:"Não atribuÃdo","No lines found for this role.":"Nenhuma linha encontrada para esta função.",claimed:"reivindicado","Claim a role before the host starts. You can take more than one.":"Reivindique uma função antes do host começar. Você pode levar mais de um.","Randomize Player Order":"Randomizar a ordem dos jogadores","Shuffles the player order once when the game starts, then continues rotating fairly.":"Embaralha a ordem dos jogadores uma vez quando o jogo começa e depois continua girando de forma justa.","PVP Format":"Formato PVP","Turn Battle":"Batalha de turno","Parallel Race":"Corrida Paralela","Players record separately. Faster players get a higher score.":"Os jogadores gravam separadamente. Jogadores mais rápidos obtêm uma pontuação mais alta.","Players take turns recording the same clip before moving to the next one.":"Os jogadores se revezam na gravação do mesmo clipe antes de passar para o próximo.","Allow Re-records":"Permitir regravações","Player Limit":"Limite de jogadores",Confirm:"Confirmar","On: players can clear their current multiplayer take and record it again before submitting.":"Ativado: os jogadores podem limpar sua tomada multijogador atual e gravá-la novamente antes de enviá-la.","DUB MOD":"MODO DUB","{count} players in room":"{count} jogadores na sala","Score Judging":"Julgamento de pontuação","Judge feedback":"Feedback do juiz","Judge score stage":"Estágio de pontuação do juiz","Preparing score":"Preparando pontuação","Your {label} is":"Seu {label} é","Final score {score}":"Pontuação final {score}",pending:"pendente",Continue:"Continuar",Duration:"Duração",Timing:"Tempo",Energy:"Energia",Pitch:"Argumento","Perfect Fit":"Ajuste perfeito","Good Fit":"Bom ajuste",Uneven:"Desigual","Too Short/Long":"Muito curto/longo","Locked In":"Trancado","On Time":"Na hora certa",Close:"Fechar","Off Beat":"Fora do ritmo","Stage Ready":"Estágio pronto",Expressive:"Expressivo",Solid:"Sólido",Flat:"Plano",Uncanny:"Estranho","Very Close":"Muito perto","Close Enough":"Perto o suficiente",Different:"Diferente","Perfect Dub":"Dublagem perfeita","Viral Performance":"Desempenho viral","Scene Stealer":"Ladrão de cena","Great Take":"Ãtima tomada","Solid Dub":"Dublagem Sólida","Getting There":"Chegando lá","Keep Practicing":"Continue praticando","Download exp
2orts":"Baixar exportações","Download My Work":"Baixe meu trabalho","Your browser needs one more tap to play this mod video.":"Seu navegador precisa de mais um toque para reproduzir este vÃdeo mod.","Continue Export":"Continuar exportação","Video subtitle option":"Opção de legenda de vÃdeo",Clean:"Limpar",Subtitles:"Legendas","Download my vertical Shorts video":"Baixe meu vÃdeo de Shorts verticais","Save Shorts":"Salvar Shorts","My Shorts":"Meus Shorts",Video:"VÃdeo","Download my landscape video":"Baixe meu vÃdeo de paisagem","Save Landscape":"Salvar paisagem","My Landscape":"Minha paisagem","Download my audio track":"Baixe minha faixa de áudio","Save Audio":"Salvar áudio","My Audio":"Meu áudio",Track:"Rastrear","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Mantenha esta página aberta enquanto se prepara. VÃdeos longos podem levar vários minutos. Quando estiver pronto, toque em Salvar para usar o menu Salvar do iPhone.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"As exportações são WebM. O Chrome é recomendado para reprodução; se precisar de MP4, converta o arquivo você mesmo.","Share exports":"Compartilhar exportações",Share:"Compartilhar","Copy share caption":"Copiar legenda de compartilhamento","Copy caption":"Copiar legenda","Rotate your phone":"Gire seu telefone","Landscape gives the video and recording controls more room.":"Paisagem dá mais espaço aos controles de vÃdeo e gravação.","Stay in portrait":"Fique no retrato","Previous run ended unexpectedly":"A execução anterior terminou inesperadamente","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike recarregado enquanto um jogo estava ativo. Copie este relatório se a interrupção ocorrer novamente.","Copy crash report":"Copiar relatório de falha","Dismiss crash report":"Ignorar relatório de falha","Page interrupted":"Página interrompida","Your room and locally saved recordings can be restored after reloading.":"Sua sala e as gravações salvas localmente podem ser restauradas após recarregar.","Reload Voxalike":"Recarregar Voxalike","ROOM CLOSING SOON":"SALA FECHANDO EM BREVE","This room has been waiting for 10 minutes.":"Esta sala está esperando há 10 minutos.","Start the game within the next 10 minutes or the room will close automatically.":"Comece o jogo nos próximos 10 minutos ou a sala fechará automaticamente.","Room closed because no game started within 20 minutes.":"Sala fechada porque nenhum jogo começou em 20 minutos.","Room closed because the host did not reconnect within 5 minutes.":"Sala fechada porque o anfitrião não se reconectou em 5 minutos.","Wait for disconnected players to reconnect":"Aguarde até que os jogadores desconectados se reconectem",Reconnecting:"Reconectando","Game server updated":"Servidor do jogo atualizado","Connection interrupted":"Conexão interrompida","Syncing the latest game server and restoring your room.":"Sincronizando o servidor de jogo mais recente e restaurando sua sala.","Your connection dropped. Trying to reconnect to your room.":"Sua conexão caiu. Tentando se reconectar ao seu quarto."}},Sw="voxalike:interface-language",nj="voxalike_locale",ij=[{value:"en",label:"English"},{value:"de",label:"Deutsch"},{value:"ru",label:"Ð ÑÑÑкий"},{value:"uk",label:"УкÑаÑнÑÑка"},{value:"nl",label:"Nederlands"},{value:"fr",label:"Français"},{value:"it",label:"Italiano"},{value:"es",label:"Español"},{value:"pt",label:"Português"}],sj={fr:{...vd.fr,...yd.fr},it:{...vd.it,...yd.it},es:{...vd.es,...yd.es},pt:{...vd.pt,...yd.pt},de:{"Interface Language":"Oberflächensprache","Choose the language used by the game interface.":"Wähle die Sprache der Spieloberfläche.",Language:"Sprache",PLAY:"SPIELEN",NEWS:"NEUIGKEITEN","DEVELOPER NEWS":"ENTWICKLER-NEWS","Voxalike News":"Voxalike-Neuigkeiten",GUIDE:"ANLEITUNG",WORKSHOP:"WORKSHOP",SUPPORTER:"SUPPORTER",SUPPORT:"UNTERSTÃTZEN","Sign in":"Anmelden","Account menu":"Kontomenü",Notifications:"Benachrichtigungen",Profile:"Profil","Account details":"Kontodetails","Edit Profile":"Profil bearbeiten",Studio:"Studio","Support Again":"Erneut unterstützen","Become a Supporter":"Supporter werden","Support Voxalike":"Voxalike unterstützen","Voxalike Player":"
2Voxalike-Spieler","Supporter profile preview":"Vorschau des Supporter-Profils","Profile preview":"Profilvorschau","6 name colors":"6 Namensfarben","3 avatar frames":"3 Avatarrahmen","4 avatar frames":"4 Avatarrahmen","3 avatar frames + 1 animated bonus frame":"3 Avatarrahmen + 1 animierter Bonusrahmen","Try on {frame}":"{frame} ausprobieren","Supporter purchase":"Supporter-Kauf","one time":"einmalig","No subscription":"Kein Abo","Animated Supporter badge":"Animiertes Supporter-Abzeichen","Checkout could not start. Please try again.":"Der Bezahlvorgang konnte nicht gestartet werden. Bitte versuche es erneut.","Checkout was canceled. Nothing was charged.":"Der Bezahlvorgang wurde abgebrochen. Es wurde nichts berechnet.","Payment is being confirmed. Your benefits will appear here automatically.":"Die Zahlung wird bestätigt. Deine Vorteile erscheinen hier automatisch.","Buy Supporter for US$5":"Supporter für US$5 kaufen","Gift code checkout is not available yet.":"Der Kauf von Geschenkcodes ist derzeit nicht verfügbar.","Payment is being confirmed. Your gift code will appear here automatically.":"Die Zahlung wird bestätigt. Dein Geschenkcode erscheint hier automatisch.","Buy Supporter or a gift code":"Supporter oder Geschenkcode kaufen","Activate on this account":"Für dieses Konto aktivieren","Supporter benefits start after payment.":"Die Supporter-Vorteile beginnen nach der Zahlung.","Buy a Supporter gift code":"Supporter-Geschenkcode kaufen","Give Supporter":"Supporter verschenken","Manage gift codes":"Geschenkcodes verwalten","SUPPORTER GIFT":"SUPPORTER-GESCHENK","Gift delivery method":"Art der Geschenkzustellung","Send to a player":"An einen Spieler senden","Delivered directly to their account":"Direkt an das Konto zugestellt","Buy gift codes":"Geschenkcodes kaufen","Share the codes yourself":"Codes selbst weitergeben","Player's Voxalike email":"Voxalike-E-Mail des Spielers","Searching...":"Suche läuft...","Find player":"Spieler finden","Could not find that player.":"Dieser Spieler wurde nicht gefunden.","Confirm recipient":"Empfänger bestätigen","Open profile":"Profil öffnen","This is your account":"Das ist dein Konto","Already has Supporter":"Hat bereits Supporter","Gift Supporter for US$5":"Supporter für 5 US$ verschenken","Number of gift codes":"Anzahl der Geschenkcodes",Total:"Gesamt","Buy {count} gift codes":"{count} Geschenkcodes kaufen","Buy 1 gift code":"1 Geschenkcode kaufen","SUPPORTER GIFTS":"SUPPORTER-GESCHENKE","{count} unused codes":"{count} unbenutzte Codes","1 unused code":"1 unbenutzter Code","Copy all unused":"Alle unbenutzten kopieren","Download unused":"Unbenutzte herunterladen","Loading gift codes...":"Geschenkcodes werden geladen...","You do not have any gift codes yet.":"Du hast noch keine Geschenkcodes.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"Die Zahlung wird bestätigt. Deine Geschenkcodes erscheinen automatisch in der Geschenkverwaltung.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"Die Zahlung wird bestätigt. Das Geschenk wird automatisch an {name} zugestellt.","Supporter was delivered to {name}.":"Supporter wurde an {name} zugestellt.","the selected player":"den ausgewählten Spieler","That player already has Supporter.":"Dieser Spieler hat bereits Supporter.","Use Activate on this account to buy Supporter for yourself.":"Nutze Für dieses Konto aktivieren, um Supporter für dich selbst zu kaufen.","That Voxalike account is no longer available.":"Dieses Voxalike-Konto ist nicht mehr verfügbar.","Give the code to any player to redeem.":"Gib den Code einem beliebigen Spieler zum Einlösen.","Your gift codes":"Deine Geschenkcodes",Redeemed:"Eingelöst","Ready to redeem":"Bereit zum Einlösen","Copy code":"Code kopieren","Redeem Supporter code":"Supporter-Code einlösen","Enter Supporter code":"Supporter-Code eingeben","Redeem code":"Code einlösen","Code redeemed. Supporter is now active.":"Code eingelöst. Supporter ist jetzt aktiv.","This code is invalid or is not ready yet.":"Dieser Code ist ungültig oder noch nicht bereit.","This code has already been redeemed.":"Dieser Code wurde bereits eingelöst.","Too many redemption attempts. Please wait a few minutes.":"Zu viele Einlöseversuche. Bitte warte einige Minuten.","Customize style":"Stil anpassen","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike wird von QHyralis, LLC betrieben, dem Zahlungsempfänger.","One payment. Permanent benef
2its.":"Einmal zahlen. Dauerhafte Vorteile.","One-time payment":"Einmalige Zahlung","Permanent Supporter badge":"Dauerhaftes Supporter-Abzeichen","Ad-free on this account":"Werbefrei mit diesem Konto","Xsolla could not start checkout. Please try again.":"Xsolla konnte den Bezahlvorgang nicht starten. Bitte versuche es erneut.","Too many checkout attempts. Please wait a few minutes.":"Zu viele Bezahlversuche. Bitte warte einige Minuten.","Supporter checkout is not available yet.":"Der Supporter-Bezahlvorgang ist noch nicht verfügbar.","Supporter is already active on this account.":"Supporter ist für dieses Konto bereits aktiv.","Payment is being confirmed. Refresh in a moment.":"Die Zahlung wird bestätigt. Aktualisiere die Seite gleich noch einmal.","Supporter active":"Supporter aktiv","Your badge and ad-free access are permanently active.":"Dein Abzeichen und der werbefreie Zugang sind dauerhaft aktiv.","Loading account...":"Konto wird geladen...","Sign in to continue":"Zum Fortfahren anmelden","Buy Now":"Jetzt kaufen","Payments are processed securely by Xsolla.":"Zahlungen werden sicher von Xsolla verarbeitet.","One purchase per account.":"Ein Kauf pro Konto.","By continuing, you agree to the":"Wenn du fortfährst, stimmst du den",Terms:"Nutzungsbedingungen",and:"und der","Privacy Policy":"Datenschutzerklärung zu","Admin Benefits Active":"Admin-Vorteile aktiv","Contributor Benefits Active":"Mitwirkenden-Vorteile aktiv","Apply for Contributor":"Als Mitwirkender bewerben","Admin Active":"Admin aktiv","Contributor Active":"Mitwirkender aktiv","Contributor Program":"Mitwirkendenprogramm","Creator Workshop":"Creator-Workshop","Sign out":"Abmelden",Community:"Community","Main menu":"Hauptmenü","Single Player":"Einzelspieler",Multiplayer:"Mehrspieler",Mods:"Mods",Settings:"Einstellungen","Install App":"App installieren","Community Contributor applications":"Bewerbungen für Community-Mitwirkende",Dismiss:"SchlieÃen","Dismiss contributor notice":"Hinweis zum Mitwirkendenprogramm schlieÃen","Limited Alpha Event":"Zeitlich begrenztes Alpha-Event","Community Contributor applications are open":"Bewerbungen für Community-Mitwirkende sind geöffnet","Earn Ad-Free access and an exclusive multiplayer badge.":"Erhalte werbefreien Zugang und ein exklusives Mehrspieler-Abzeichen.","Ends Sep 8, 2026 at 11:59 PM PT":"Endet am 8. September 2026 um 23:59 Uhr PT","View details":"Details anzeigen",Back:"Zurück",Menu:"Menü","New Run":"Neue Runde","SINGLE PLAYER":"EINZELSPIELER",MULTIPLAYER:"MEHRSPIELER","Preparing Stage":"Bühne wird vorbereitet","My Dub Premiere":"Premiere meiner Vertonung","Judge Score":"Jurywertung","Final Score":"Endwertung","Dub Stage":"Vertonungsbühne","Create or Join":"Erstellen oder beitreten","Recent Dub":"Letzte Vertonung","PLAYER OPTIONS":"SPIELEROPTIONEN","Press F11 for fullscreen.":"Drücke F11 für den Vollbildmodus.","MOD MANAGER":"MOD-VERWALTUNG","Audio Settings":"Audioeinstellungen","Control game audio levels.":"Passe die Lautstärke des Spiels an.","Master Volume":"Gesamtlautstärke","Sound Effects":"Soundeffekte","Background Music":"Hintergrundmusik","Input Device":"Eingabegerät","Voice Processing":"Stimmverarbeitung","Microphone Volume":"Mikrofonlautstärke","Changes recording playback and exports. Scoring is unaffected.":"Ãndert die Wiedergabe und den Export der Aufnahme. Die Wertung bleibt unverändert.",Natural:"Natürlich","Browser noise reduction":"Browser-Rauschunterdrückung","Clean voice":"Saubere Stimme","Best clarity for headphones and quiet rooms.":"Beste Klarheit mit Kopfhörern und in ruhigen Räumen.","Uses only your browser's built-in noise reduction.":"Verwendet nur die integrierte Rauschunterdrückung deines Browsers.","No browser or Voxalike voice processing.":"Keine Stimmverarbeitung durch den Browser oder Voxalike.","Raw microphone active":"Unbearbeitetes Mikrofon aktiv","Browser and Voxalike voice processing are disabled; no calibration is needed.":"Die Stimmverarbeitung von Browser und Voxalike ist deaktiviert; keine Kalibrierung ist erforderlich.","Record a short sample to hear your microphone without voice processing.":"Nimm eine kurze Probe auf, um dein Mikrofon ohne Stimmverarbeitung zu hören.","Listen to the sample to hear your microphone without voice processing.":"Höre dir die Probe an, um dein Mikrofon ohne Stimmverarbeitung zu hören.","Browser noise reduction active":"Browser-Rauschunterdrückung aktiv","Your browser handles noise reduction automatically; no calibration is needed.":"Dein Browser übernimmt die Rauschunterdrückung automatisch; keine Kalibrierung ist erforderlich.","Record a short sample to hear your browser's built-in noise reduction.":"Nimm eine kurze Probe auf, um die integrierte Rauschunterdrückung deines Browsers zu hören.","Listen to the sample to hear your browser's built-in noise reduction.":"Höre dir die Probe an, um die integrierte Rauschunterdrückung deines Browsers zu hören.","Play
2sample":"Probe abspielen","Stronger cleanup for fans, keyboards and noisy rooms.":"Stärkere Bereinigung bei Lüftern, Tastaturen und lauten Räumen.",Default:"Standard","Microphone {number}":"Mikrofon {number}","Mic Check":"Mikrofontest","Recording Sync":"Aufnahmesynchronisierung","Left: earlier. Right: later.":"Links: früher. Rechts: später.","Voice timing":"Stimmen-Timing",Earlier:"Früher",Later:"Später","Input Level":"Eingangspegel","Stay Silent":"Bitte still bleiben","Measuring background noise for 2 seconds.":"Hintergrundgeräusche werden 2 Sekunden lang gemessen.","Speak Normally":"Normal sprechen","Record your voice at normal volume for 3-5 seconds.":"Sprich 3â5 Sekunden lang mit normaler Lautstärke.","Stay silent for 2 seconds. Measuring background noise.":"Bleibe 2 Sekunden still. Hintergrundgeräusche werden gemessen.","Speak at your normal volume for 3-5 seconds.":"Sprich 3â5 Sekunden lang mit normaler Lautstärke.","This mic check is applied to future dubbing recordings.":"Dieser Mikrofontest wird auf zukünftige Vertonungen angewendet.","Compare the samples, then apply this cleanup to future recordings.":"Vergleiche die Beispiele und wende die Bereinigung auf zukünftige Aufnahmen an.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"Deine gespeicherte Mikrofonkalibrierung wird in diesem Browser auf zukünftige Vertonungen angewendet.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"Bleibe 2 Sekunden still und sprich danach 3â5 Sekunden normal. Das Ergebnis kann für die Rauschunterdrückung im Spiel verwendet werden.","Use Mic Check to connect":"Mikrofontest zum Verbinden verwenden","Requesting microphone":"Mikrofonzugriff wird angefordert","Microphone Ready":"Mikrofon bereit","Permission denied":"Zugriff verweigert","Microphone Unavailable":"Mikrofon nicht verfügbar","Microphone may be occupied":"Mikrofon wird möglicherweise von einer anderen App verwendet","Selected device unavailable":"Ausgewähltes Gerät nicht verfügbar","Device disconnected":"Gerät getrennt","Calibration saved · mic reconnects when needed":"Kalibrierung gespeichert · Mikrofon wird bei Bedarf verbunden","Microphone could not start":"Mikrofon konnte nicht gestartet werden","Mic calibration applied":"Mikrofonkalibrierung angewendet","Mic calibration not applied":"Mikrofonkalibrierung nicht angewendet","Noise cleanup is active for future recordings.":"Rauschunterdrückung ist für zukünftige Aufnahmen aktiv.","Run Mic Check and apply it to improve noise cleanup.":"Führe den Mikrofontest aus und wende ihn an, um die Rauschunterdrückung zu verbessern.",Applied:"Angewendet","Ready to apply":"Bereit zum Anwenden","Noise:":"Rauschen:","Volume:":"Lautstärke:","Peaks:":"Spitzen:",Raw:"Original",Cleaned:"Bereinigt",Retake:"Wiederholen","Apply Mic Check":"Mikrofontest anwenden","Mic is too loud":"Mikrofon ist zu laut","Background noise is high":"Hintergrundgeräusche sind zu laut","Voice is quiet":"Stimme ist leise","Voice is loud":"Stimme ist laut","Mic sounds ready":"Mikrofon klingt bereit","Start Mic Check":"Mikrofontest starten","Connecting Mic...":"Mikrofon wird verbunden...","Stop Recording":"Aufnahme stoppen",Record:"Aufnehmen","Retry Mic":"Mikrofon erneut verbinden",Replay:"Wiedergeben","My Take":"Meine Aufnahme",Retry:"Neu aufnehmen",Next:"Weiter",Finish:"Beenden",Skip:"Ãberspringen","GET READY":"BEREIT MACHEN",RECORDING:"AUFNAHME","CLIP {current} / {total}":"CLIP {current} / {total}","Jump ahead to another clip":"Zu einem anderen Clip springen","Replay original clip":"Originalclip wiedergeben","Finish pack":"Mod abschlieÃen","Go to next clip":"Zum nächsten Clip",Start:"Starten","Starting...":"Wird gestartet...","Could not start this mod":"Diese Mod konnte nicht gestartet werden","Copy start error details":"Startfehler kopieren","Creator:":"Ersteller:","Characters:":"Figuren:","No Dub Mods Yet":"Noch keine Vertonungs-Mods","Subscribe to a pack from the Workshop, then start your game.":"Abonniere eine Mod im Workshop und starte danach das Spiel.","Browse Workshop":"Workshop durchsuchen","My Mods":"Meine Mods","Refresh Mods":"Mods aktualisieren","Refreshing Mods...":"Mods werden aktualisiert...","Refreshing...":"Wird aktualisiert...","Mods are up to date.":"Die Mods sind auf dem neuesten Stand.","{count} mods":"{count} Mods","{count} / syncing Workshop":"{count} / Workshop wird synchronisiert","{count} / {name} judges in use":"{count} / Jury â{name}â wird verwendet","{count} / default judges in use":"{count} / Standardjury wird verwendet","Local mod import options":"Optionen zum lokalen Mod-Import","Import local file":"Lokale Datei importieren","Dub Mod ZIP":"Vertonungs-Mod-ZIP","Dub Mod Folder":"Vertonungs-Mod-Ordner","Judge Mod ZIP":"Jury-Mod-ZIP","Search by mod, creator, character, or type":"Nach Mod, Ersteller, Figur oder Typ suchen","Search mods":"Mods durchsuchen","Clear search":"Suche löschen","Syncing Workshop subscriptions":"Workshop-Abonnements werden synchronisiert","Checking which installed mods belong to your account.":"Installierte Mods werden deinem Konto zugeordnet.","No mods yet":"Noch keine Mods","Subscribe to a pack from the Workshop, then start a game in":"Abonniere eine Mod im Workshop und starte danach ein Spiel im",Solo:"Einzelspieler",or:"oder"," game.":"-Modus.",'N
2o mods match "{query}"':"Keine Mods entsprechen â{query}â","Clear Search":"Suche löschen","View installation progress in Mods":"Installationsfortschritt unter Mods anzeigen","View in Mods":"Unter Mods anzeigen","Show clip scores":"Clipwertungen anzeigen","Reconnect microphone and record":"Mikrofon neu verbinden und aufnehmen","Play your recorded take with background audio":"Deine Aufnahme mit Hintergrundton abspielen","Play your recorded take":"Deine Aufnahme abspielen","Background volume":"Hintergrundlautstärke",Background:"Hintergrund",Cancel:"Abbrechen","Report Player":"Spieler melden","Mod feedback":"Mod-Feedback","Report this mod":"Mod melden","Report reason":"Meldegrund","Incorrect role information":"Falsche Rollenangaben","Incorrect language":"Falsche Sprache","Broken or misleading mod":"Defekter oder irreführender Mod","Inappropriate content":"Unangemessener Inhalt","Copyright or rights issue":"Urheber- oder Rechteproblem","Spam or abuse":"Spam oder Missbrauch","Describe the problem":"Beschreibe das Problem","Send Report":"Meldung senden","Sending...":"Wird gesendet...","Report received.":"Meldung erhalten.","Report could not be submitted.":"Meldung konnte nicht gesendet werden.","Close report dialog":"Meldedialog schlieÃen","Reported Player":"Gemeldeter Spieler","Report Type":"Meldegrund","Choose a violation type":"Verstoà auswählen",Description:"Beschreibung","Describe what happened so moderators can review the report.":"Beschreibe, was passiert ist, damit Moderatoren die Meldung prüfen können.","At least 10 characters.":"Mindestens 10 Zeichen.","Submit Report":"Meldung senden","Harassment / Abuse":"Belästigung / Missbrauch","Hate Speech":"Hassrede",Spam:"Spam","Inappropriate Content":"Unangemessene Inhalte",Other:"Sonstiges","Clip Scores":"Clipwertungen","{completed} / {total} clips completed":"{completed} / {total} Clips abgeschlossen",Detail:"Details","Back to final score":"Zurück zur Endwertung","No playable dub video is available.":"Kein abspielbares Vertonungsvideo verfügbar.","Play my dubbed work":"Meine Vertonung abspielen","PUBLIC MULTIPLAYER":"ÃFFENTLICHER MEHRSPIELER","Public Lobby":"Ãffentliche Lobby","Refresh public rooms":"Ãffentliche Räume aktualisieren","Multiplayer Lobby":"Mehrspieler-Lobby","Refresh rooms":"Räume aktualisieren","All Languages":"Alle Sprachen",Status:"Status","All Rooms":"Alle Räume","Waiting Only":"Nur wartende Räume","In Progress":"Läuft",Results:"Ergebnisse",Waiting:"Wartet","No public rooms match these filters.":"Keine öffentlichen Räume entsprechen diesen Filtern.","Loading public rooms...":"Ãffentliche Räume werden geladen...","No rooms match these filters.":"Keine Räume entsprechen diesen Filtern.","Loading rooms...":"Räume werden geladen...","Room visibility":"Raumsichtbarkeit",Private:"Privat","Private Room":"Privater Raum",Public:"Ãffentlich","Room Language":"Raumsprache","Sign in to create a public room":"Melde dich an, um einen öffentlichen Raum zu erstellen","Waiting for host to choose a mod":"Der Host wählt noch eine Mod",Join:"Beitreten","View Profile":"Profil ansehen","Host must start: {count}s":"Host muss starten: {count}s","Room Chat":"Raumchat","No messages yet.":"Noch keine Nachrichten.","Message the room":"Nachricht an den Raum","Reconnecting chat...":"Chat wird wieder verbunden...","Send message":"Nachricht senden","Open room chat":"Raumchat öffnen","Close room chat":"Raumchat schlieÃen","Players online":"Spieler online","{count} unread messages":"{count} ungelesene Nachrichten","{name} disconnected.":"Verbindung von {name} getrennt.","{name} reconnected.":"{name} ist wieder verbunden.","{name} left the room.":"{name} hat den Raum verlassen.","This public room has been waiting for 50 minutes.":"Dieser öffentliche Raum wartet seit 50 Minuten.","Player Name":"Spielername","Create Room":"Raum erstellen","Creating...":"Wird erstellt...","Room Code":"Raumcode","Join Room":"Raum beitreten","Joining...":"Beitritt...","Leave Room":"Raum verlassen","ROOM CODE":"RAUMCODE","Copy Code":"Code kopieren","Copy Invite Link":"Einladungslink kopieren",Copied:"Kopiert",Host:"Host",Ready:"Bereit",Rendering:"Wird gerendert","Not rendered":"Nicht gerendert","Waiting â click to switch rendering":"Wartet â klicken, um das Rendern zu wechseln","Not Ready":"Nicht bereit","Kick Player":"Spieler entfernen","Host actions for {name}":"Host-Aktionen für {name}","Empty player slot":"Freier Spielerplatz","Ready Up":"Bereit machen","Cancel Ready":"Bereitschaft zurücknehmen","Installing room mod":"Raum-Mod wird installiert","Installing room mod {percent}%":"Raum-Mod wird installiert: {percent}%","Start Game":"Spiel starten","Starting in {count}":"Start in {count}","How multiplayer mods sync":"So werden Mehrspieler-Mods synchronisiert","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"Workshop-Abonnements werden für angemeldete Spieler automatisch installiert. Lokale Mods müssen von allen Personen im Raum heruntergeladen und importiert werden.","Change Mod":"Mod wechseln","Choose a Mod":"Mod auswählen","Import a mod f
2irst":"Zuerst eine Mod importieren","Multiplayer mode":"Mehrspielermodus","Co-op":"Koop","Rotate turns or let players claim open clips.":"Wechselt euch ab oder lasst Spieler freie Clips übernehmen.","Everyone records the same clips and compares scores.":"Alle nehmen dieselben Clips auf und vergleichen ihre Wertungen.","Co-op Turn Mode":"Koop-Zugmodus","Co-op Assignment":"Koop-Zuweisung","Rotate turns, claim clips, or claim roles.":"Wechselt euch ab oder wählt Clips oder Rollen.","Round Robin":"Reihum","Claim Mode":"Freie Clipwahl","Role Claim":"Rollenwahl","Co-op · Role Claim":"Koop · Rollenwahl","Choose Roles":"Rollen wählen","Back to room":"Zurück zum Raum","All roles are assigned.":"Alle Rollen sind verteilt.","Start Dubbing":"Synchronisation starten","Waiting for the host to start dubbing.":"Warte auf den Start durch den Host.","Syncing role selection":"Rollenauswahl wird synchronisiert","This mod has no roles to claim":"Dieses Mod hat keine auswählbaren Rollen","Claim every role before starting":"Wählt vor dem Start alle Rollen","Choose your roles":"Wählt eure Rollen","Choose your roles.":"Wählt eure Rollen.",Claim:"Rolle übernehmen",Release:"Rolle freigeben","Returned to lobby.":"Zurück in der Lobby.","Could not start role selection.":"Die Rollenauswahl konnte nicht gestartet werden.","Could not return to lobby.":"Die Rückkehr zur Lobby ist fehlgeschlagen.","Could not update role.":"Die Rolle konnte nicht aktualisiert werden.","Session complete":"Runde abgeschlossen","Click a role to claim it. Click again to release it.":"Klicke auf eine Rolle, um sie zu übernehmen. Klicke erneut, um sie freizugeben.","Preview lines":"Zeilen ansehen","Loading lines...":"Zeilen werden geladen...","LINE PREVIEW":"ZEILENVORSCHAU","{count} lines":"{count} Zeilen","Filter lines by role":"Zeilen nach Rolle filtern",All:"Alle",Unassigned:"Nicht zugewiesen","No lines found for this role.":"Für diese Rolle wurden keine Zeilen gefunden.",claimed:"vergeben","Claim a role before the host starts. You can take more than one.":"Wähle vor dem Start eine Rolle. Du kannst mehrere übernehmen.","Randomize Player Order":"Spielerreihenfolge mischen","Shuffles the player order once when the game starts, then continues rotating fairly.":"Mischt die Spielerreihenfolge beim Start einmal und wechselt danach fair reihum.","PVP Format":"PVP-Format","Turn Battle":"Rundenkampf","Parallel Race":"Paralleles Rennen","Players record separately. Faster players get a higher score.":"Spieler nehmen getrennt auf. Schnellere Spieler erhalten eine höhere Wertung.","Players take turns recording the same clip before moving to the next one.":"Alle nehmen nacheinander denselben Clip auf, bevor es zum nächsten geht.","Allow Re-records":"Neue Aufnahmen erlauben","Player Limit":"Spielerlimit",Confirm:"Bestätigen","On: players can clear their current multiplayer take and record it again before submitting.":"Aktiv: Spieler können ihre aktuelle Mehrspieleraufnahme löschen und vor dem Absenden neu aufnehmen.","DUB MOD":"VERTONUNGS-MOD","{count} players in room":"{count} Spieler im Raum","Score Judging":"Bewertung","Judge feedback":"Juryfeedback","Judge score stage":"Jurywertung","Preparing score":"Wertung wird vorbereitet","Your {label} is":"Dein Wert für {label}:","Final score {score}":"Endwertung {score}",pending:"ausstehend",Continue:"Fortfahren",Duration:"Dauer",Timing:"Timing",Energy:"Energie",Pitch:"Tonhöhe","Perfect Fit":"Perfekt passend","Good Fit":"Gut passend",Uneven:"UngleichmäÃig","Too Short/Long":"Zu kurz/lang","Locked In":"Perfekt im Takt","On Time":"Im Takt",Close:"SchlieÃen","Off Beat":"Nicht im Takt","Stage Ready":"Bühnenreif",Expressive:"Ausdrucksstark",Solid:"Solide",Flat:"Ausdruckslos",Uncanny:"Verblüffend ähnlich","Very Close":"Sehr ähnlich","Close Enough":"Ãhnlich genug",Different:"Anders","Perfect Dub":"Perfekte Vertonung","Viral Performance":"Viraler Auftritt","Scene Stealer":"Szenenstar","Great Take":"GroÃartige Aufnahme","Solid Dub":"Solide Vertonung","Getting There":"Auf dem richtigen Weg","Keep Practicing":"Weiter üben","Download exp
2orts":"Exporte herunterladen","Download My Work":"Meine Arbeit herunterladen","Your browser needs one more tap to play this mod video.":"Dein Browser braucht einen weiteren Tipp, um das Mod-Video abzuspielen.","Continue Export":"Export fortsetzen","Video subtitle option":"Untertiteloption für Videos",Clean:"Ohne Untertitel",Subtitles:"Untertitel","Download my vertical Shorts video":"Mein vertikales Shorts-Video herunterladen","Save Shorts":"Shorts speichern","My Shorts":"Meine Shorts",Video:"Video","Download my landscape video":"Mein Video im Querformat herunterladen","Save Landscape":"Querformat speichern","My Landscape":"Mein Querformat","Download my audio track":"Meine Audiospur herunterladen","Save Audio":"Audio speichern","My Audio":"Mein Audio",Track:"Spur","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Lasse diese Seite während der Vorbereitung geöffnet. Lange Videos können mehrere Minuten dauern. Tippe anschlieÃend auf Speichern, um das iPhone-Menü zu öffnen.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"Exporte werden als WebM erstellt. Chrome wird für die Wiedergabe empfohlen. Wenn du MP4 benötigst, konvertiere die Datei bitte selbst.","Share exports":"Exporte teilen",Share:"Teilen","Copy share caption":"Beitragstext kopieren","Copy caption":"Text kopieren","Rotate your phone":"Drehe dein Smartphone","Landscape gives the video and recording controls more room.":"Im Querformat ist mehr Platz für Video und Aufnahmesteuerung.","Stay in portrait":"Im Hochformat bleiben","Previous run ended unexpectedly":"Die vorherige Runde wurde unerwartet beendet","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike wurde während einer laufenden Runde neu geladen. Kopiere diesen Bericht, falls die Unterbrechung erneut auftritt.","Copy crash report":"Absturzbericht kopieren","Dismiss crash report":"Absturzbericht schlieÃen","Page interrupted":"Seite wurde unterbrochen","Your room and locally saved recordings can be restored after reloading.":"Dein Raum und lokal gespeicherte Aufnahmen können nach dem Neuladen wiederhergestellt werden.","Reload Voxalike":"Voxalike neu laden","ROOM CLOSING SOON":"RAUM WIRD BALD GESCHLOSSEN","This room has been waiting for 10 minutes.":"Dieser Raum wartet seit 10 Minuten.","Start the game within the next 10 minutes or the room will close automatically.":"Starte das Spiel innerhalb der nächsten 10 Minuten, sonst wird der Raum automatisch geschlossen.","Room closed because no game started within 20 minutes.":"Der Raum wurde geschlossen, weil innerhalb von 20 Minuten kein Spiel gestartet wurde.","Room closed because the host did not reconnect within 5 minutes.":"Der Raum wurde geschlossen, weil der Host nicht innerhalb von 5 Minuten zurückgekehrt ist.","Wait for disconnected players to reconnect":"Warte, bis getrennte Spieler wieder verbunden sind",Reconnecting:"Verbindung wird wiederhergestellt","Game server updated":"Spielserver aktualisiert","Connection interrupted":"Verbindung unterbrochen","Syncing the latest game server and restoring your room.":"Der aktuelle Spielserver wird synchronisiert und dein Raum wiederhergestellt.","Your connection dropped. Trying to reconnect to your room.":"Deine Verbindung wurde getrennt. Die Verbindung zum Raum wird wiederhergestellt."},nl:{"Interface Language":"Interfacetaal","Choose the language used by the game interface.":"Kies de taal van de game-interface.",Language:"Taal",PLAY:"SPELEN",NEWS:"NIEUWS","DEVELOPER NEWS":"ONTWIKKELAARSNIEUWS","Voxalike News":"Voxalike-nieuws",GUIDE:"HANDLEIDING",WORKSHOP:"WORKSHOP",SUPPORTER:"SUPPORTER",SUPPORT:"STEUN","Sign in":"Inloggen","Account menu":"Accountmenu",Notifications:"Meldingen",Profile:"Profiel","Account details":"Accountgegevens","Edit Profile":"Profiel bewerken",Studio:"Studio","Support Again":"Opnieuw steunen","Become a Supporter":"Word Supporter","Support Voxalike":"Steun Voxalike","Voxalike Player":"Vox
2alike-speler","Supporter profile preview":"Voorbeeld van Supporter-profiel","Profile preview":"Profielvoorbeeld","6 name colors":"6 naamkleuren","3 avatar frames":"3 avatarframes","4 avatar frames":"4 avatarframes","3 avatar frames + 1 animated bonus frame":"3 avatarframes + 1 geanimeerd bonusframe","Try on {frame}":"{frame} uitproberen","Supporter purchase":"Supporter-aankoop","one time":"eenmalig","No subscription":"Geen abonnement","Animated Supporter badge":"Geanimeerde Supporter-badge","Checkout could not start. Please try again.":"Het afrekenen kon niet worden gestart. Probeer het opnieuw.","Checkout was canceled. Nothing was charged.":"Het afrekenen is geannuleerd. Er is niets in rekening gebracht.","Payment is being confirmed. Your benefits will appear here automatically.":"De betaling wordt bevestigd. Je voordelen verschijnen hier automatisch.","Buy Supporter for US$5":"Koop Supporter voor US$5","Gift code checkout is not available yet.":"Het afrekenen voor cadeaucode is nog niet beschikbaar.","Payment is being confirmed. Your gift code will appear here automatically.":"De betaling wordt bevestigd. Je cadeaucode verschijnt hier automatisch.","Buy Supporter or a gift code":"Koop Supporter of een cadeaucode","Activate on this account":"Activeren op dit account","Supporter benefits start after payment.":"Supporter-voordelen beginnen na betaling.","Buy a Supporter gift code":"Koop een Supporter-cadeaucode","Give Supporter":"Supporter cadeau doen","Manage gift codes":"Cadeaucodes beheren","SUPPORTER GIFT":"SUPPORTER-CADEAU","Gift delivery method":"Bezorgmethode voor cadeau","Send to a player":"Naar een speler sturen","Delivered directly to their account":"Direct op hun account geleverd","Buy gift codes":"Cadeaucodes kopen","Share the codes yourself":"Deel de codes zelf","Player's Voxalike email":"Voxalike-e-mailadres van speler","Searching...":"Zoeken...","Find player":"Speler zoeken","Could not find that player.":"Die speler kon niet worden gevonden.","Confirm recipient":"Ontvanger bevestigen","Open profile":"Profiel openen","This is your account":"Dit is jouw account","Already has Supporter":"Heeft Supporter al","Gift Supporter for US$5":"Supporter cadeau doen voor US$5","Number of gift codes":"Aantal cadeaucodes",Total:"Totaal","Buy {count} gift codes":"{count} cadeaucodes kopen","Buy 1 gift code":"1 cadeaucode kopen","SUPPORTER GIFTS":"SUPPORTER-CADEAUS","{count} unused codes":"{count} ongebruikte codes","1 unused code":"1 ongebruikte code","Copy all unused":"Alle ongebruikte kopiëren","Download unused":"Ongebruikte downloaden","Loading gift codes...":"Cadeaucodes laden...","You do not have any gift codes yet.":"Je hebt nog geen cadeaucodes.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"De betaling wordt bevestigd. Je cadeaucodes verschijnen automatisch in het cadeau-overzicht.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"De betaling wordt bevestigd. Het cadeau wordt automatisch aan {name} geleverd.","Supporter was delivered to {name}.":"Supporter is aan {name} geleverd.","the selected player":"de geselecteerde speler","That player already has Supporter.":"Die speler heeft Supporter al.","Use Activate on this account to buy Supporter for yourself.":"Gebruik Activeren op dit account om Supporter voor jezelf te kopen.","That Voxalike account is no longer available.":"Dat Voxalike-account is niet meer beschikbaar.","Give the code to any player to redeem.":"Geef de code aan een speler om in te wisselen.","Your gift codes":"Je cadeau-codes",Redeemed:"Ingewisseld","Ready to redeem":"Klaar om in te wisselen","Copy code":"Code kopiëren","Redeem Supporter code":"Supporter-code inwisselen","Enter Supporter code":"Voer Supporter-code in","Redeem code":"Code inwisselen","Code redeemed. Supporter is now active.":"Code ingewisseld. Supporter is nu actief.","This code is invalid or is not ready yet.":"Deze code is ongeldig of nog niet gereed.","This code has already been redeemed.":"Deze code is al ingewisseld.","Too many redemption attempts. Please wait a few minutes.":"Te veel inwisselpogingen. Wacht een paar minuten.","Customize style":"Stijl aanpassen","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike wordt beheerd door QHyralis, LLC, de ontvanger van de betaling.","One payment. Permanent benef
2its.":"Eén betaling. Permanente voordelen.","One-time payment":"Eenmalige betaling","Permanent Supporter badge":"Permanente Supporter-badge","Ad-free on this account":"Geen advertenties op dit account","Xsolla could not start checkout. Please try again.":"Xsolla kon de betaling niet starten. Probeer het opnieuw.","Too many checkout attempts. Please wait a few minutes.":"Te veel betaalpogingen. Wacht een paar minuten.","Supporter checkout is not available yet.":"Supporter-betaling is nog niet beschikbaar.","Supporter is already active on this account.":"Supporter is al actief op dit account.","Payment is being confirmed. Refresh in a moment.":"De betaling wordt bevestigd. Vernieuw de pagina zo meteen.","Supporter active":"Supporter actief","Your badge and ad-free access are permanently active.":"Je badge en reclamevrije toegang zijn permanent actief.","Loading account...":"Account laden...","Sign in to continue":"Log in om verder te gaan","Buy Now":"Nu kopen","Payments are processed securely by Xsolla.":"Betalingen worden veilig verwerkt door Xsolla.","One purchase per account.":"Eén aankoop per account.","By continuing, you agree to the":"Door verder te gaan, ga je akkoord met de",Terms:"Voorwaarden",and:"en het","Privacy Policy":"Privacybeleid","Admin Benefits Active":"Adminvoordelen actief","Contributor Benefits Active":"Bijdragersvoordelen actief","Apply for Contributor":"Aanmelden als bijdrager","Admin Active":"Admin actief","Contributor Active":"Bijdrager actief","Contributor Program":"Bijdragersprogramma","Creator Workshop":"Creator Workshop","Sign out":"Uitloggen",Community:"Community","Main menu":"Hoofdmenu","Single Player":"Singleplayer",Multiplayer:"Multiplayer",Mods:"Mods",Settings:"Instellingen","Install App":"App installeren","Community Contributor applications":"Aanmeldingen voor Community Contributor",Dismiss:"Sluiten","Dismiss contributor notice":"Melding over bijdragers sluiten","Limited Alpha Event":"Tijdelijk alpha-evenement","Community Contributor applications are open":"Aanmeldingen voor Community Contributor zijn geopend","Earn Ad-Free access and an exclusive multiplayer badge.":"Krijg reclamevrije toegang en een exclusieve multiplayerbadge.","Ends Sep 8, 2026 at 11:59 PM PT":"Eindigt op 8 september 2026 om 23:59 PT","View details":"Details bekijken",Back:"Terug",Menu:"Menu","New Run":"Nieuwe ronde","SINGLE PLAYER":"SINGLEPLAYER",MULTIPLAYER:"MULTIPLAYER","Preparing Stage":"Podium voorbereiden","My Dub Premiere":"Première van mijn dub","Judge Score":"Juryscore","Final Score":"Eindscore","Dub Stage":"Dubpodium","Create or Join":"Aanmaken of deelnemen","Recent Dub":"Recente dub","PLAYER OPTIONS":"SPELEROPTIES","Press F11 for fullscreen.":"Druk op F11 voor volledig scherm.","MOD MANAGER":"MODBEHEER","Audio Settings":"Audio-instellingen","Control game audio levels.":"Pas het geluidsniveau van de game aan.","Master Volume":"Hoofdvolume","Sound Effects":"Geluidseffecten","Background Music":"Achtergrondmuziek","Input Device":"Invoerapparaat","Voice Processing":"Spraakverwerking","Microphone Volume":"Microfoonvolume","Changes recording playback and exports. Scoring is unaffected.":"Past de opnameweergave en exports aan. De score blijft ongewijzigd.",Natural:"Natuurlijk","Browser noise reduction":"Browserruisonderdrukking","Clean voice":"Heldere stem","Best clarity for headphones and quiet rooms.":"De beste helderheid voor koptelefoons en stille ruimtes.","Uses only your browser's built-in noise reduction.":"Gebruikt alleen de ingebouwde ruisonderdrukking van je browser.","No browser or Voxalike voice processing.":"Geen spraakverwerking door de browser of Voxalike.","Raw microphone active":"Onbewerkte microfoon actief","Browser and Voxalike voice processing are disabled; no calibration is needed.":"Spraakverwerking van de browser en Voxalike is uitgeschakeld; kalibratie is niet nodig.","Record a short sample to hear your microphone without voice processing.":"Neem een kort fragment op om je microfoon zonder spraakverwerking te horen.","Listen to the sample to hear your microphone without voice processing.":"Luister naar het fragment om je microfoon zonder spraakverwerking te horen.","Browser noise reduction active":"Browserruisonderdrukking actief","Your browser handles noise reduction automatically; no calibration is needed.":"Je browser verwerkt ruisonderdrukking automatisch; kalibratie is niet nodig.","Record a short sample to hear your browser's built-in noise reduction.":"Neem een kort fragment op om de ingebouwde ruisonderdrukking van je browser te horen.","Listen to the sample to hear your browser's built-in noise reduction.":"Luister naar het fragment om de ingebouwde ruisonderdrukking van je browser te horen.","Play
2sample":"Fragment afspelen","Stronger cleanup for fans, keyboards and noisy rooms.":"Sterkere ruisonderdrukking voor ventilatoren, toetsenborden en rumoerige ruimtes.",Default:"Standaard","Microphone {number}":"Microfoon {number}","Mic Check":"Microfoontest","Recording Sync":"Opnamesynchronisatie","Left: earlier. Right: later.":"Links: eerder. Rechts: later.","Voice timing":"Timing van stem",Earlier:"Eerder",Later:"Later","Input Level":"Ingangsniveau","Stay Silent":"Blijf stil","Measuring background noise for 2 seconds.":"Achtergrondgeluid wordt 2 seconden gemeten.","Speak Normally":"Praat normaal","Record your voice at normal volume for 3-5 seconds.":"Neem je stem 3â5 seconden op met normaal volume.","Stay silent for 2 seconds. Measuring background noise.":"Blijf 2 seconden stil. Achtergrondgeluid wordt gemeten.","Speak at your normal volume for 3-5 seconds.":"Praat 3â5 seconden op je normale volume.","This mic check is applied to future dubbing recordings.":"Deze microfoontest wordt toegepast op toekomstige dubopnamen.","Compare the samples, then apply this cleanup to future recordings.":"Vergelijk de voorbeelden en pas deze ruisonderdrukking toe op toekomstige opnamen.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"Je opgeslagen microfoonkalibratie wordt in deze browser toegepast op toekomstige dubopnamen.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"Blijf 2 seconden stil en praat daarna 3â5 seconden normaal. Het resultaat kan worden gebruikt voor ruisonderdrukking in de game.","Use Mic Check to connect":"Gebruik de microfoontest om verbinding te maken","Requesting microphone":"Microfoontoegang aanvragen","Microphone Ready":"Microfoon gereed","Permission denied":"Toegang geweigerd","Microphone Unavailable":"Microfoon niet beschikbaar","Microphone may be occupied":"De microfoon is mogelijk in gebruik","Selected device unavailable":"Geselecteerd apparaat niet beschikbaar","Device disconnected":"Apparaat losgekoppeld","Calibration saved · mic reconnects when needed":"Kalibratie opgeslagen · microfoon maakt indien nodig opnieuw verbinding","Microphone could not start":"Microfoon kon niet worden gestart","Mic calibration applied":"Microfoonkalibratie toegepast","Mic calibration not applied":"Microfoonkalibratie niet toegepast","Noise cleanup is active for future recordings.":"Ruisonderdrukking is actief voor toekomstige opnamen.","Run Mic Check and apply it to improve noise cleanup.":"Voer de microfoontest uit en pas deze toe om de ruisonderdrukking te verbeteren.",Applied:"Toegepast","Ready to apply":"Klaar om toe te passen","Noise:":"Ruis:","Volume:":"Volume:","Peaks:":"Pieken:",Raw:"Origineel",Cleaned:"Opgeschoond",Retake:"Opnieuw opnemen","Apply Mic Check":"Microfoontest toepassen","Mic is too loud":"Microfoon is te luid","Background noise is high":"Achtergrondgeluid is te luid","Voice is quiet":"Stem is zacht","Voice is loud":"Stem is luid","Mic sounds ready":"Microfoon klinkt goed","Start Mic Check":"Microfoontest starten","Connecting Mic...":"Microfoon verbinden...","Stop Recording":"Opname stoppen",Record:"Opnemen","Retry Mic":"Microfoon opnieuw proberen",Replay:"Opnieuw afspelen","My Take":"Mijn opname",Retry:"Opnieuw",Next:"Volgende",Finish:"Voltooien",Skip:"Overslaan","GET READY":"MAAK JE KLAAR",RECORDING:"OPNAME","CLIP {current} / {total}":"CLIP {current} / {total}","Jump ahead to another clip":"Ga naar een andere clip","Replay original clip":"Originele clip opnieuw afspelen","Finish pack":"Mod voltooien","Go to next clip":"Naar de volgende clip",Start:"Starten","Starting...":"Starten...","Could not start this mod":"Deze mod kon niet worden gestart","Copy start error details":"Details van startfout kopiëren","Creator:":"Maker:","Characters:":"Personages:","No Dub Mods Yet":"Nog geen Dub Mods","Subscribe to a pack from the Workshop, then start your game.":"Abonneer je op een pack in de Workshop en start daarna je game.","Browse Workshop":"Workshop bekijken","My Mods":"Mijn mods","Refresh Mods":"Mods vernieuwen","Refreshing Mods...":"Mods vernieuwen...","Refreshing...":"
2Vernieuwen...","Mods are up to date.":"De mods zijn bijgewerkt.","{count} mods":"{count} mods","{count} / syncing Workshop":"{count} / Workshop synchroniseren","{count} / {name} judges in use":"{count} / jury {name} in gebruik","{count} / default judges in use":"{count} / standaardjury in gebruik","Local mod import options":"Opties voor het importeren van lokale mods","Import local file":"Lokaal bestand importeren","Dub Mod ZIP":"Dub Mod-ZIP","Dub Mod Folder":"Dub Mod-map","Judge Mod ZIP":"Judge Mod-ZIP","Search by mod, creator, character, or type":"Zoek op mod, maker, personage of type","Search mods":"Mods zoeken","Clear search":"Zoekopdracht wissen","Syncing Workshop subscriptions":"Workshop-abonnementen synchroniseren","Checking which installed mods belong to your account.":"Controleren welke geïnstalleerde mods bij je account horen.","No mods yet":"Nog geen mods","Subscribe to a pack from the Workshop, then start a game in":"Abonneer je op een pack in de Workshop en start daarna een game in",Solo:"Solo",or:"of"," game.":"-modus.",'No mods match "{query}"':'Geen mods gevonden voor "{query}"',"Clear Search":"Zoekopdracht wissen","View installation progress in Mods":"Installatievoortgang bekijken bij Mods","Vie
2w in Mods":"Bekijken bij Mods","Show clip scores":"Clipscores tonen","Reconnect microphone and record":"Microfoon opnieuw verbinden en opnemen","Play your recorded take with background audio":"Speel je opname af met achtergrondgeluid","Play your recorded take":"Speel je opname af","Background volume":"Achtergrondvolume",Background:"Achtergrond",Cancel:"Annuleren","Report Player":"Speler rapporteren","Mod feedback":"Feedback over mod","Report this mod":"Mod rapporteren","Report reason":"Reden van melding","Incorrect role information":"Onjuiste rolinformatie","Incorrect language":"Onjuiste taal","Broken or misleading mod":"Kapotte of misleidende mod","Inappropriate content":"Ongepaste inhoud","Copyright or rights issue":"Auteursrecht of rechtenprobleem","Spam or abuse":"Spam of misbruik","Describe the problem":"Beschrijf het probleem","Send Report":"Melding verzenden","Sending...":"Bezig met verzenden...","Report received.":"Melding ontvangen.","Report could not be submitted.":"Melding kon niet worden verzonden.","Close report dialog":"Meldingsvenster sluiten","Reported Player":"Gerapporteerde speler","Report Type":"Type melding","Choose a violation type":"Kies een type overtreding",Description:"Beschrijving","Describe what happened so moderators can review the report.":"Beschrijf wat er is gebeurd, zodat moderators de melding kunnen beoordelen.","At least 10 characters.":"Minimaal 10 tekens.","Submit Report":"Melding indienen","Harassment / Abuse":"Intimidatie / misbruik","Hate Speech":"Haatzaaien",Spam:"Spam","Inappropriate Content":"Ongepaste inhoud",Other:"Overig","Clip Scores":"Clipscores","{completed} / {total} clips completed":"{completed} / {total} clips voltooid",Detail:"Details","Back to final score":"Terug naar eindscore","No playable dub video is available.":"Er is geen afspeelbare dubvideo beschikbaar.","Play my dubbed work":"Mijn dub afspelen","PUBLIC MULTIPLAYER":"OPENBARE MULTIPLAYER","Public Lobby":"Openbare lobby","Refresh public rooms":"Openbare kamers vernieuwen","Multiplayer Lobby":"Multiplayerlobby","Refresh rooms":"Kamers vernieuwen","All Languages":"Alle talen",Status:"Status","All Rooms":"Alle kamers","Waiting Only":"Alleen wachtend","In Progress":"Bezig",Results:"Resultaten",Waiting:"Wachten","No public rooms match these filters.":"Geen openbare kamers passen bij deze filters.","Loading public rooms...":"Openbare kamers laden...","No rooms match these filters.":"Geen kamers passen bij deze filters.","Loading rooms...":"Kamers laden...","Room visibility":"Zichtbaarheid van kamer",Private:"Privé","Private Room":"Privékamer",Public:"Openbaar","Room Language":"Kamertaal","Sign in to create a public room":"Log in om een openbare kamer te maken","Waiting for host to choose a mod":"Wachten tot de host een mod kiest",Join:"Deelnemen","View Profile":"Profiel bekijken","Host must start: {count}s":"Host moet starten: {count}s","Room Chat":"Kamerchat","No messages yet.":"Nog geen berichten.","Message the room":"Stuur een bericht","Reconnecting chat...":"Chat opnieuw verbinden...","Send message":"Bericht verzenden","Open room chat":"Kamerchat openen","Close room chat":"Kamerchat sluiten","Players online":"Spelers online","{count} unread messages":"{count} ongelezen berichten","{name} disconnected.":"Verbinding met {name} verbroken.","{name} reconnected.":"{name} is opnieuw verbonden.","{name} left the room.":"{name} heeft de kamer verlaten.","This public room has been waiting for 50 minutes.":"Deze openbare kamer wacht al 50 minuten.","Player Name":"Spelersnaam","Create Room":"Kamer aanmaken","Creating...":"Aanmaken...","Room Code":"Kamercode","Join Room":"Deelnemen aan kamer","Joining...":"Deelnemen...","Leave Room":"Kamer verlaten","ROOM CODE":"KAMERCODE","Copy Code":"Code kopiëren","Copy Invite Link":"Uitnodigingslink kopiëren",Copied:"Gekopieerd",Host:"Host",Ready:"Gereed",Rendering:"Wordt gerenderd","Not rendered":"Niet gerenderd","Waiting â click to switch rendering":"Wachten â klik om het renderen te wisselen","Not Ready":"Niet gereed","Kick Player":"Speler verwijderen","Host actions for {name}":"Hostacties voor {name}","Empty player slot":"Lege spelersplek","Ready Up":"Gereed melden","Cancel Ready":"Gereedmelding annuleren","Installing room mod":"Kamermod installeren","Installing room mod {percent}%":"Kamermod installeren: {percent}%","Start Game":"Game starten","Starting in {count}":"Start over {count}","How multiplayer mods sync":"Hoe multiplayer-mods synchroniseren","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"Workshop-abonnementen worden automatisch geïnstalleerd voor ingelogde spelers. Lokale mods moeten door iedereen in de kamer worden gedownload en geïmporteerd.","Change Mod":"Mod wijzigen","Choose a Mod":"Kies een mod","Import a mod f
2irst":"Importeer eerst een mod","Multiplayer mode":"Multiplayermodus","Co-op":"Co-op","Rotate turns or let players claim open clips.":"Wissel beurten af of laat spelers beschikbare clips kiezen.","Everyone records the same clips and compares scores.":"Iedereen neemt dezelfde clips op en vergelijkt de scores.","Co-op Turn Mode":"Co-opbeurtmodus","Co-op Assignment":"Co-opverdeling","Rotate turns, claim clips, or claim roles.":"Speel om de beurt of kies clips of rollen.","Round Robin":"Om de beurt","Claim Mode":"Kiesmodus","Role Claim":"Rollen kiezen","Co-op · Role Claim":"Co-op · Rollen kiezen","Choose Roles":"Kies rollen","Back to room":"Terug naar kamer","All roles are assigned.":"Alle rollen zijn verdeeld.","Start Dubbing":"Start inspreken","Waiting for the host to start dubbing.":"Wachten tot de host begint.","Syncing role selection":"Rollen worden gesynchroniseerd","This mod has no roles to claim":"Deze mod heeft geen rollen om te kiezen","Claim every role before starting":"Kies alle rollen voordat je begint","Choose your roles":"Kies je rollen","Choose your roles.":"Kies je rollen.",Claim:"Rol kiezen",Release:"Rol vrijgeven","Returned to lobby.":"Terug in de lobby.","Could not start role selection.":"De rolkeuze kon niet worden gestart.","Could not return to lobby.":"Terugkeren naar de lobby is mislukt.","Could not update role.":"De rol kon niet worden bijgewerkt.","Session complete":"Ronde voltooid","Click a role to claim it. Click again to release it.":"Klik op een rol om die te kiezen. Klik nogmaals om die vrij te geven.","Preview lines":"Regels bekijken","Loading lines...":"Regels laden...","LINE PREVIEW":"REGELVOORBEELD","{count} lines":"{count} regels","Filter lines by role":"Regels filteren op rol",All:"Alles",Unassigned:"Niet toegewezen","No lines found for this role.":"Geen regels voor deze rol gevonden.",claimed:"gekozen","Claim a role before the host starts. You can take more than one.":"Kies een rol voordat de host start. Je kunt er meer dan één kiezen.","Randomize Player Order":"Spelersvolgorde willekeurig maken","Shuffles the player order once when the game starts, then continues rotating fairly.":"Schudt de spelersvolgorde eenmaal bij de start en wisselt daarna eerlijk door.","PVP Format":"PVP-indeling","Turn Battle":"Beurtenstrijd","Parallel Race":"Parallelle race","Players record separately. Faster players get a higher score.":"Spelers nemen afzonderlijk op. Snellere spelers krijgen een hogere score.","Players take turns recording the same clip before moving to the next one.":"Spelers nemen om de beurt dezelfde clip op voordat ze naar de volgende gaan.","Allow Re-records":"Nieuwe opnamen toestaan","Player Limit":"Spelerslimiet",Confirm:"Bevestigen","On: players can clear their current multiplayer take and record it again before submitting.":"Aan: spelers kunnen hun huidige multiplayeropname wissen en opnieuw opnemen voordat ze deze insturen.","DUB MOD":"DUB MOD","{count} players in room":"{count} spelers in de kamer","Score Judging":"Scorebeoordeling","Judge feedback":"Juryfeedback","Judge score stage":"Juryscore","Preparing score":"Score voorbereiden","Your {label} is":"Je {label} is","Final score {score}":"Eindscore {score}",pending:"in afwachting",Continue:"Doorgaan",Duration:"Duur",Timing:"Timing",Energy:"Energie",Pitch:"Toonhoogte","Perfect Fit":"Perfect passend","Good Fit":"Goed passend",Uneven:"Ongelijkmatig","Too Short/Long":"Te kort/lang","Locked In":"Perfect op tempo","On Time":"Op tijd",Close:"Sluiten","Off Beat":"Uit de maat","Stage Ready":"Podiumklaar",Expressive:"Expressief",Solid:"Sterk",Flat:"Vlak",Uncanny:"Verbluffend","Very Close":"Zeer dichtbij","Close Enough":"Dicht genoeg",Different:"Anders","Perfect Dub":"Perfecte dub","Viral Performance":"Virale uitvoering","Scene Stealer":"Blikvanger","Great Take":"Geweldige opname","Solid Dub":"Sterke dub","Getting There":"Goed op weg","Keep Practicing":"Blijf oefenen","Download exports":"Exports downloaden","Download My Work":"Mijn werk downloaden","Your browser needs one more tap to play this mod video.":"Tik nog een keer om de modvideo in je browser af te spelen.","Continue Export":"Export voortzetten","Video subtitle option":"Ondertiteloptie voor video",Clean:"Zonder ondertitels",Subt
2itles:"Ondertitels","Download my vertical Shorts video":"Mijn verticale Shorts-video downloaden","Save Shorts":"Shorts opslaan","My Shorts":"Mijn Shorts",Video:"Video","Download my landscape video":"Mijn liggende video downloaden","Save Landscape":"Liggende video opslaan","My Landscape":"Mijn liggende video","Download my audio track":"Mijn audiotrack downloaden","Save Audio":"Audio opslaan","My Audio":"Mijn audio",Track:"Track","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Houd deze pagina open tijdens het voorbereiden. Lange video's kunnen enkele minuten duren. Tik wanneer alles klaar is op Opslaan om het iPhone-menu te gebruiken.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"Exports zijn WebM-bestanden. Chrome wordt aanbevolen voor afspelen; als je MP4 nodig hebt, moet je het bestand zelf converteren.","Share exports":"Exports delen",Share:"Delen","Copy share caption":"Deeltekst kopiëren","Copy caption":"Tekst kopiëren","Rotate your phone":"Draai je telefoon","Landscape gives the video and recording controls more room.":"In liggende stand is er meer ruimte voor de video- en opnamebediening.","Stay in portrait":"In staande stand blijven","Previous run ended unexpectedly":"De vorige ronde is onverwacht beëindigd","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike werd opnieuw geladen terwijl een game actief was. Kopieer dit rapport als de onderbreking opnieuw optreedt.","Copy crash report":"Crashrapport kopiëren","Dismiss crash report":"Crashrapport sluiten","Page interrupted":"Pagina onderbroken","Your room and locally saved recordings can be restored after reloading.":"Je kamer en lokaal opgeslagen opnamen kunnen na het herladen worden hersteld.","Reload Voxalike":"Voxalike herladen","ROOM CLOSING SOON":"KAMER SLUIT BINNENKORT","This room has been waiting for 10 minutes.":"Deze kamer wacht al 10 minuten.","Start the game within the next 10 minutes or the room will close automatically.":"Start de game binnen 10 minuten, anders wordt de kamer automatisch gesloten.","Room closed because no game started within 20 minutes.":"De kamer is gesloten omdat er binnen 20 minuten geen game is gestart.","Room closed because the host did not reconnect within 5 minutes.":"De kamer is gesloten omdat de host niet binnen 5 minuten opnieuw verbinding maakte.","Wait for disconnected players to reconnect":"Wacht tot spelers met een verbroken verbinding opnieuw verbinden",Reconnecting:"Opnieuw verbinden","Game server updated":"Gameserver bijgewerkt","Connection interrupted":"Verbinding onderbroken","Syncing the latest game server and restoring your room.":"De nieuwste gameserver wordt gesynchroniseerd en je kamer wordt hersteld.","Your connection dropped. Trying to reconnect to your room.":"Je verbinding is verbroken. We proberen opnieuw verbinding te maken met je kamer."},ru:{"Interface Language":"ЯзÑк инÑеÑÑейÑа","Choose the language used by the game interface.":"ÐÑбеÑиÑе ÑзÑк игÑового инÑеÑÑейÑа.",Language:"ЯзÑк",PLAY:"ÐÐÐ ÐТЬ",NEWS:"ÐÐÐÐСТÐ","DEVELOPER NEWS":"ÐÐÐÐСТРРÐÐÐ ÐÐÐТЧÐÐÐÐ","Voxalike News":"ÐовоÑÑи Voxalike",GUIDE:"РУÐÐÐÐÐСТÐÐ",WORKSHOP:"ÐÐСТÐРСÐÐЯ",SUPPORTER:"СÐÐÐСÐÐ ",SUPPORT:"ÐÐÐÐÐÐ ÐÐТЬ","Sign in":"ÐойÑи","Account menu":"ÐÐµÐ½Ñ Ð°ÐºÐºÐ°ÑнÑа",Notifications:"УведомлениÑ",Profile:"ÐÑоÑилÑ","Account details":"ÐаннÑе аккаÑнÑа","Edit Profile":"ÐзмениÑÑ Ð¿ÑоÑилÑ",Studio:"СÑÑдиÑ","Support Again":"ÐоддеÑжаÑÑ Ñнова","Become a Supporter":"СÑаÑÑ ÑпонÑоÑом","Support Voxalike":"ÐоддеÑжаÑÑ Voxalike","Voxalike Player":"ÐгÑок Voxalike","Supporter profile preview":"ÐÑедпÑоÑмоÑÑ Ð¿ÑоÑÐ¸Ð»Ñ Supporter","Profile preview":"ÐÑедпÑоÑмоÑÑ Ð¿ÑоÑилÑ","6 name colors":"6 ÑвеÑов имени","3 avatar frames":"3 Ñамки аваÑаÑа","4 avatar frames":"4 Ñамки аваÑаÑа","3 avatar frames + 1 animated bonus frame":"3 Ñамки аваÑаÑа + 1 бонÑÑÐ½Ð°Ñ Ð°Ð½Ð¸Ð¼Ð¸ÑÐ¾Ð²Ð°Ð½Ð½Ð°Ñ Ñамка","Try on {frame}":"ÐÑимеÑиÑÑ {frame}","Supporter purchase":"ÐокÑпка Supporter","one time":"Ñазово","No subscription":"Ðез подпиÑки","Animated Supporter badge":"ÐнимиÑованнÑй знаÑок Supporter","Checkout could not start. Please try again.":"Ðе ÑдалоÑÑ Ð½Ð°ÑаÑÑ Ð¾Ð¿Ð»Ð°ÑÑ. ÐопÑобÑйÑе еÑÑ Ñаз.","Checkout was canceled. Nothing was charged.":"ÐплаÑа оÑменена. ÐенÑги не ÑпиÑанÑ.","Payment is being confirmed. Your benefits will appear here automatically.":"ÐлаÑÑж подÑвеÑждаеÑÑÑ. ÐаÑи пÑеимÑÑеÑÑва поÑвÑÑÑÑ Ð·Ð´ÐµÑÑ Ð°Ð²ÑомаÑиÑеÑки.","Buy Supporter for US$5":"ÐÑпиÑÑ Supporter за US$5","Gift code
2checkout is not available yet.":"ÐокÑпка подаÑоÑного кода пока недоÑÑÑпна.","Payment is being confirmed. Your gift code will appear here automatically.":"ÐлаÑÑж подÑвеÑждаеÑÑÑ. ÐодаÑоÑнÑй код поÑвиÑÑÑ Ð·Ð´ÐµÑÑ Ð°Ð²ÑомаÑиÑеÑки.","Buy Supporter or a gift code":"ÐÑпиÑÑ Supporter или подаÑоÑнÑй код","Activate on this account":"ÐкÑивиÑоваÑÑ Ð½Ð° ÑÑом аккаÑнÑе","Supporter benefits start after payment.":"ÐÑеимÑÑеÑÑва Supporter акÑивиÑÑÑÑÑÑ Ð¿Ð¾Ñле оплаÑÑ.","Buy a Supporter gift code":"ÐÑпиÑÑ Ð¿Ð¾Ð´Ð°ÑоÑнÑй код Supporter","Give Supporter":"ÐодаÑиÑÑ Supporter","Manage gift codes":"УпÑавление подаÑоÑнÑми кодами","SUPPORTER GIFT":"ÐÐÐÐÐ ÐÐ SUPPORTER","Gift delivery method":"СпоÑоб доÑÑавки подаÑка","Send to a player":"ÐÑпÑавиÑÑ Ð¸Ð³ÑокÑ","Delivered directly to their account":"ÐÑÐ´ÐµÑ Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¾ пÑÑмо в аккаÑнÑ","Buy gift codes":"ÐÑпиÑÑ Ð¿Ð¾Ð´Ð°ÑоÑнÑе кодÑ","Share the codes yourself":"ÐеÑедайÑе ÐºÐ¾Ð´Ñ ÑамоÑÑоÑÑелÑно","Player's Voxalike email":"ÐлекÑÑÐ¾Ð½Ð½Ð°Ñ Ð¿Ð¾ÑÑа игÑока в Voxalike","Searching...":"ÐоиÑк...","Find player":"ÐайÑи игÑока","Could not find that player.":"Ðе ÑдалоÑÑ Ð½Ð°Ð¹Ñи ÑÑого игÑока.","Confirm recipient":"ÐодÑвеÑдиÑе полÑÑаÑелÑ","Open profile":"ÐÑкÑÑÑÑ Ð¿ÑоÑилÑ","This is your account":"ÐÑо Ð²Ð°Ñ Ð°ÐºÐºÐ°ÑнÑ","Already has Supporter":"Supporter Ñже акÑивен","Gift Supporter for US$5":"ÐодаÑиÑÑ Supporter за 5 $","Number of gift codes":"ÐолиÑеÑÑво подаÑоÑнÑÑ
кодов",Total:"ÐÑого","Buy {count} gift codes":"ÐÑпиÑÑ Ð¿Ð¾Ð´Ð°ÑоÑнÑе кодÑ: {count}","Buy 1 gift code":"ÐÑпиÑÑ 1 подаÑоÑнÑй код","SUPPORTER GIFTS":"ÐÐÐÐÐ ÐÐ SUPPORTER","{count} unused codes":"ÐеиÑполÑзованнÑÑ
кодов: {count}","1 unused code":"1 неиÑполÑзованнÑй код","Copy all unused":"СкопиÑоваÑÑ Ð²Ñе неиÑполÑзованнÑе","Download unused":"СкаÑаÑÑ Ð½ÐµÐ¸ÑполÑзованнÑе","Loading gift codes...":"ÐагÑÑзка подаÑоÑнÑÑ
кодов...","You do not have any gift codes yet.":"У Ð²Ð°Ñ Ð¿Ð¾ÐºÐ° Ð½ÐµÑ Ð¿Ð¾Ð´Ð°ÑоÑнÑÑ
кодов.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"ÐлаÑÑж подÑвеÑждаеÑÑÑ. ÐодаÑоÑнÑе ÐºÐ¾Ð´Ñ Ð°Ð²ÑомаÑиÑеÑки поÑвÑÑÑÑ Ð² Ñазделе подаÑков.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"ÐлаÑÑж подÑвеÑждаеÑÑÑ. ÐодаÑок бÑÐ´ÐµÑ Ð°Ð²ÑомаÑиÑеÑки доÑÑавлен игÑÐ¾ÐºÑ {name}.","Supporter was delivered to {name}.":"Supporter доÑÑавлен игÑÐ¾ÐºÑ {name}.","the selected player":"вÑбÑÐ°Ð½Ð½Ð¾Ð¼Ñ Ð¸Ð³ÑокÑ","That player already has Supporter.":"У ÑÑого игÑока Ñже еÑÑÑ Supporter.","Use Activate on this account to buy Supporter for yourself.":"ЧÑÐ¾Ð±Ñ ÐºÑпиÑÑ Supporter Ð´Ð»Ñ ÑебÑ, иÑполÑзÑйÑе ÐºÐ½Ð¾Ð¿ÐºÑ Ð°ÐºÑиваÑии на ÑÑом аккаÑнÑе.","That Voxalike account is no longer available.":"ÐÑÐ¾Ñ Ð°ÐºÐºÐ°ÑÐ½Ñ Voxalike болÑÑе недоÑÑÑпен.","Give the code to any player to redeem.":"ÐеÑедайÑе код лÑÐ±Ð¾Ð¼Ñ Ð¸Ð³ÑÐ¾ÐºÑ Ð´Ð»Ñ Ð°ÐºÑиваÑии.","Your gift codes":"ÐаÑи подаÑоÑнÑе кодÑ",Redeemed:"ÐкÑивиÑован","Ready to redeem":"ÐоÑов к акÑиваÑии","Copy code":"ÐопиÑоваÑÑ ÐºÐ¾Ð´","Redeem Supporter code":"ÐкÑивиÑоваÑÑ ÐºÐ¾Ð´ Supporter","Enter Supporter code":"ÐведиÑе код Supporter","Redeem code":"ÐкÑивиÑоваÑÑ ÐºÐ¾Ð´","Code redeemed. Supporter is now active.":"Ðод акÑивиÑован. Supporter ÑепеÑÑ Ð´Ð¾ÑÑÑпен.","This code is invali
2d or is not ready yet.":"ÐÑÐ¾Ñ ÐºÐ¾Ð´ недейÑÑвиÑелен или еÑÑ Ð½Ðµ гоÑов.","This code has already been redeemed.":"ÐÑÐ¾Ñ ÐºÐ¾Ð´ Ñже бÑл акÑивиÑован.","Too many redemption attempts. Please wait a few minutes.":"СлиÑком много попÑÑок акÑиваÑии. ÐодождиÑе неÑколÑко минÑÑ.","Customize style":"ÐаÑÑÑоиÑÑ ÑÑилÑ","Voxalike is operated by QHyralis, LLC, the payment recipient.":"ÐпеÑаÑÐ¾Ñ Voxalike и полÑÑаÑÐµÐ»Ñ Ð¿Ð»Ð°Ñежа â QHyralis, LLC.","One payment. Permanent benefits.":"Ðдин плаÑÑж. ÐоÑÑоÑннÑе пÑеимÑÑеÑÑва.","One-time payment":"РазовÑй плаÑÑж","Permanent Supporter badge":"ÐоÑÑоÑннÑй знаÑок ÑпонÑоÑа","Ad-free on this account":"Ðез ÑÐµÐºÐ»Ð°Ð¼Ñ Ð´Ð»Ñ ÑÑого аккаÑнÑа","Xsolla could not start checkout. Please try again.":"Xsolla не ÑдалоÑÑ Ð½Ð°ÑаÑÑ Ð¾Ð¿Ð»Ð°ÑÑ. ÐопÑобÑйÑе еÑÑ Ñаз.","Too many checkout attempts. Please wait a few minutes.":"СлиÑком много попÑÑок оплаÑÑ. ÐодождиÑе неÑколÑко минÑÑ.","Supporter checkout is not available yet.":"ÐплаÑа Supporter пока недоÑÑÑпна.","Supporter is already active on this account.":"Supporter Ñже акÑивен Ð´Ð»Ñ ÑÑого аккаÑнÑа.","Payment is being confirmed. Refresh in a moment.":"ÐлаÑÑж подÑвеÑждаеÑÑÑ. ÐбновиÑе ÑÑÑаниÑÑ ÑеÑез неÑколÑко ÑекÑнд.","Supporter active":"Supporter акÑивен","Your badge and ad-free access are permanently active.":"ÐÐ°Ñ Ð·Ð½Ð°Ñок и доÑÑÑп без ÑÐµÐºÐ»Ð°Ð¼Ñ Ð°ÐºÑивиÑÐ¾Ð²Ð°Ð½Ñ Ð½Ð°Ð²Ñегда.","Loading account...":"ÐагÑÑзка аккаÑнÑа...","Sign in to continue":"ÐойдиÑе, ÑÑÐ¾Ð±Ñ Ð¿ÑодолжиÑÑ","Buy Now":"ÐÑпиÑÑ ÑейÑаÑ","Payments are processed securely by Xsolla.":"ÐлаÑежи безопаÑно обÑабаÑÑваÑÑÑÑ Xsolla.","One purchase per account.":"Ðдна покÑпка на аккаÑнÑ.","By continuing, you agree to the":"ÐÑодолжаÑ, Ð²Ñ Ð¿ÑинимаеÑе",Terms:"УÑловиÑ",and:"и","Privacy Policy":"ÐолиÑÐ¸ÐºÑ ÐºÐ¾Ð½ÑиденÑиалÑноÑÑи","Admin Benefits Active":"ÐÑеимÑÑеÑÑва админиÑÑÑаÑоÑа акÑивнÑ","Contributor Benefits Active":"ÐÑеимÑÑеÑÑва ÑÑаÑÑника акÑивнÑ","Apply for Contributor":"СÑаÑÑ ÑÑаÑÑником","Admin Active":"ÐдминиÑÑÑаÑÐ¾Ñ Ð°ÐºÑивен","Contributor Active":"УÑаÑÑник акÑивен","Contributor Program":"ÐÑогÑамма ÑÑаÑÑников","Creator Workshop":"ÐаÑÑеÑÑÐºÐ°Ñ Ð°Ð²ÑоÑа","Sign out":"ÐÑйÑи",Community:"СообÑеÑÑво","Main menu":"Ðлавное менÑ","Single Player":"ÐдиноÑÐ½Ð°Ñ Ð¸Ð³Ñа",Multiplayer:"СеÑÐµÐ²Ð°Ñ Ð¸Ð³Ñа",Mods:"ÐодÑ",Settings:"ÐаÑÑÑойки","Install App":"УÑÑановиÑÑ Ð¿Ñиложение","Community Contributor applications":"ÐаÑвки в пÑогÑÐ°Ð¼Ð¼Ñ ÑÑаÑÑников ÑообÑеÑÑва",Dismiss:"ÐакÑÑÑÑ","Dismiss contributor notice":"ÐакÑÑÑÑ Ñведомление о пÑогÑамме ÑÑаÑÑников","Limited Alpha Event":"СобÑÑие огÑаниÑенной алÑÑа-веÑÑии","Community Contributor applications are open":"ÐÑкÑÑÑ Ð¿ÑиÑм заÑвок Ð¾Ñ ÑÑаÑÑников ÑообÑеÑÑва","Earn Ad-Free access and an exclusive multiplayer badge.":"ÐолÑÑиÑе доÑÑÑп без ÑÐµÐºÐ»Ð°Ð¼Ñ Ð¸ ÑкÑклÑзивнÑй ÑеÑевой знаÑок.","Ends Sep 8, 2026 at 11:59 PM PT":"Ðо 8 ÑенÑÑбÑÑ 2026 г., 23:59 PT","View details":"ÐодÑобнее",Back:"Ðазад",Menu:"ÐенÑ","New Run":"ÐÐ¾Ð²Ð°Ñ Ð¸Ð³Ñа","SINGLE PLAYER":"ÐÐÐÐÐЧÐÐЯ ÐÐÐ Ð",MULTIPLAYER:"СÐТÐÐÐЯ ÐÐÐ Ð","Preparing Stage":"ÐодгоÑовка ÑÑенÑ","My Dub Premiere":"ÐÑемÑеÑа моей озвÑÑки","Judge Score":"ÐÑенка ÑÑдей","Final Score":"ÐÑоговÑй ÑÑÑÑ","Dub Stage":"СÑена озвÑÑки","Create or Join":"СоздаÑÑ Ð¸Ð»Ð¸ войÑи","Recent Dub":"ÐедавнÑÑ Ð¾Ð·Ð²ÑÑка","PLAYER OPTIONS":"ÐÐСТРÐÐÐÐ ÐÐÐ ÐÐÐ","Press F11 for fullscreen.":"ÐажмиÑе F11 Ð´Ð»Ñ Ð¿Ð¾Ð»Ð½Ð¾ÑкÑанного Ñежима.","MOD MANAGER":"УÐÐ ÐÐÐÐÐÐÐ ÐÐÐÐÐÐ","Audio
2Settings":"ÐаÑÑÑойки звÑка","Control game audio levels.":"ÐаÑÑÑойÑе гÑомкоÑÑÑ Ð·Ð²Ñка в игÑе.","Master Volume":"ÐбÑÐ°Ñ Ð³ÑомкоÑÑÑ","Sound Effects":"ÐвÑковÑе ÑÑÑекÑÑ","Background Music":"Ð¤Ð¾Ð½Ð¾Ð²Ð°Ñ Ð¼ÑзÑка","Input Device":"УÑÑÑойÑÑво ввода","Voice Processing":"ÐбÑабоÑка голоÑа","Microphone Volume":"ÐÑомкоÑÑÑ Ð¼Ð¸ÐºÑоÑона","Changes recording playback and exports. Scoring is unaffected.":"ÐенÑÐµÑ Ð³ÑомкоÑÑÑ Ð·Ð°Ð¿Ð¸Ñи пÑи воÑпÑоизведении и ÑкÑпоÑÑе. Ðа оÑÐµÐ½ÐºÑ Ð½Ðµ влиÑеÑ.",Natural:"ÐÑÑеÑÑвенно","Browser noise reduction":"ШÑмоподавление бÑаÑзеÑа","Clean voice":"ЧиÑÑÑй голоÑ","Best clarity for headphones and quiet rooms.":"ÐакÑималÑÐ½Ð°Ñ ÑÑноÑÑÑ Ð´Ð»Ñ Ð½Ð°ÑÑников и ÑиÑ
иÑ
помеÑений.","Uses only your browser's built-in noise reduction.":"ÐÑполÑзÑÐµÑ ÑолÑко вÑÑÑоенное ÑÑмоподавление бÑаÑзеÑа.","No browser or Voxalike voice processing.":"Ðез обÑабоÑки голоÑа бÑаÑзеÑом или Voxalike.","Raw microphone active":"ÐеобÑабоÑаннÑй микÑоÑон вклÑÑÑн","Browser and Voxalike voice processing are disabled; no calibration is needed.":"ÐбÑабоÑка голоÑа бÑаÑзеÑом и Voxalike оÑклÑÑена; калибÑовка не ÑÑебÑеÑÑÑ.","Record a short sample to hear your microphone without voice processing.":"ÐапиÑиÑе коÑоÑкий обÑазеÑ, ÑÑÐ¾Ð±Ñ ÑÑлÑÑаÑÑ Ð¼Ð¸ÐºÑоÑон без обÑабоÑки голоÑа.","Listen to the sample to hear your microphone without voice processing.":"ÐÑоÑлÑÑайÑе обÑазеÑ, ÑÑÐ¾Ð±Ñ ÑÑлÑÑаÑÑ Ð¼Ð¸ÐºÑоÑон без обÑабоÑки голоÑа.","Browser noise reduction active":"ШÑмоподавление бÑаÑзеÑа вклÑÑено","Your browser handles noise reduction automatically; no calibration is needed.":"ÐÑаÑÐ·ÐµÑ Ð°Ð²ÑомаÑиÑеÑки обÑабаÑÑÐ²Ð°ÐµÑ ÑÑм; калибÑовка не ÑÑебÑеÑÑÑ.","Record a short sample to hear your browser's built-in noise reduction.":"ÐапиÑиÑе коÑоÑкий обÑазеÑ, ÑÑÐ¾Ð±Ñ Ð¿ÑовеÑиÑÑ Ð²ÑÑÑоенное ÑÑмоподавление бÑаÑзеÑа.","Listen to the sample to hear your browser's built-in noise reduction.":"ÐÑоÑлÑÑайÑе обÑазеÑ, ÑÑÐ¾Ð±Ñ Ð¾ÑениÑÑ Ð²ÑÑÑоенное ÑÑмоподавление бÑаÑзеÑа.","Play sample":"ÐоÑпÑоизвеÑÑи обÑазеÑ","Stronger cleanup for fans, keyboards and noisy rooms.":"Ðолее ÑилÑÐ½Ð°Ñ Ð¾ÑиÑÑка Ð¾Ñ Ð²ÐµÐ½ÑилÑÑоÑов, клавиаÑÑÑÑ Ð¸ ÑÑма в помеÑении.",Default:"Ðо ÑмолÑаниÑ","Microphone {number}":"ÐикÑоÑон {number}","Mic Check":"ÐÑовеÑка микÑоÑона","Recording Sync":"СинÑ
ÑонизаÑÐ¸Ñ Ð·Ð°Ð¿Ð¸Ñи","Left: earlier. Right: later.":"Ðлево: ÑанÑÑе. ÐпÑаво: позже.","Voice timing":"СинÑ
ÑонизаÑÐ¸Ñ Ð³Ð¾Ð»Ð¾Ñа",Earlier:"РанÑÑе",Later:"Ðозже","Input Level":"УÑÐ¾Ð²ÐµÐ½Ñ Ð²Ñ
ода","Stay Silent":"СоÑ
ÑанÑйÑе ÑиÑинÑ","Measuring background noise for 2 seconds.":"ÐзмеÑение Ñонового ÑÑма в ÑеÑение 2 ÑекÑнд.","Speak Normally":"ÐовоÑиÑе обÑÑнÑм голоÑом","Record your voice at normal volume for 3-5 seconds.":"ÐовоÑиÑе Ñ Ð¾Ð±ÑÑной гÑомкоÑÑÑÑ 3â5 ÑекÑнд.","Stay silent for 2 seconds. Measuring background noise.":"СоÑ
ÑанÑйÑе ÑиÑÐ¸Ð½Ñ 2 ÑекÑндÑ. ÐзмеÑÑем ÑоновÑй ÑÑм.","Speak at your normal volume for 3-5 seconds.":"ÐовоÑиÑе Ñ Ð¾Ð±ÑÑной гÑомкоÑÑÑÑ 3â5 ÑекÑнд.","This mic check is applied to future dubbing recordings.":"ÐÑа наÑÑÑойка микÑоÑона пÑименÑеÑÑÑ Ðº бÑдÑÑим запиÑÑм озвÑÑки.","Compare the samples, then apply this cleanup to future recordings.":"СÑавниÑе обÑазÑÑ Ð¸ пÑимениÑе оÑиÑÑÐºÑ Ðº бÑдÑÑим запиÑÑм.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"СоÑ
ÑанÑÐ½Ð½Ð°Ñ ÐºÐ°Ð»Ð¸Ð±Ñовка пÑименÑеÑÑÑ Ðº бÑдÑÑим запиÑÑм озвÑÑки в ÑÑом бÑаÑзеÑе.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"СоÑ
ÑанÑйÑе ÑиÑÐ¸Ð½Ñ 2 ÑекÑндÑ, заÑем говоÑиÑе обÑÑнÑм голоÑом 3â5 ÑекÑнд. РезÑлÑÑÐ°Ñ Ð¼Ð¾Ð¶Ð½Ð¾ пÑимениÑÑ Ð´Ð»Ñ ÑÑÐ¼Ð¾Ð¿Ð¾Ð´Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð² игÑе.","Use Mic Check to connect":"ÐÑполÑзÑйÑе пÑовеÑÐºÑ Ð¼Ð¸ÐºÑоÑона Ð´Ð»Ñ Ð¿Ð¾Ð´ÐºÐ»ÑÑениÑ","Requesting microphone":"ÐапÑаÑиваем доÑÑÑп к микÑоÑонÑ","Microphone Ready":"ÐикÑоÑон гоÑов","Permission denied":"ÐоÑÑÑп запÑеÑÑн","Microphone Unavailable":"ÐикÑоÑон недоÑÑÑпен","Microphone may be occupied":"ÐикÑоÑон Ð¼Ð¾Ð¶ÐµÑ Ð¸ÑполÑзоваÑÑÑÑ Ð´ÑÑгим пÑиложением","Selected device unavailable":"ÐÑбÑанное ÑÑÑÑойÑÑво недоÑÑÑпно","Device disconnected":"УÑÑÑойÑÑво оÑклÑÑено","Calibration saved · mic reconnects when needed":"ÐалибÑовка ÑоÑ
Ñанена · микÑоÑон подклÑÑаеÑÑÑ Ð¿Ð¾ Ð
2½ÐµÐ¾Ð±Ñ
одимоÑÑи","Microphone could not start":"Ðе ÑдалоÑÑ Ð·Ð°Ð¿ÑÑÑиÑÑ Ð¼Ð¸ÐºÑоÑон","Mic calibration applied":"ÐалибÑовка микÑоÑона пÑименена","Mic calibration not applied":"ÐалибÑовка микÑоÑона не пÑименена","Noise cleanup is active for future recordings.":"ШÑмоподавление акÑивно Ð´Ð»Ñ Ð±ÑдÑÑиÑ
запиÑей.","Run Mic Check and apply it to improve noise cleanup.":"ÐапÑÑÑиÑе пÑовеÑÐºÑ Ð¼Ð¸ÐºÑоÑона и пÑимениÑе ÑезÑлÑÑÐ°Ñ Ð´Ð»Ñ ÑлÑÑÑÐµÐ½Ð¸Ñ ÑÑмоподавлениÑ.",Applied:"ÐÑименено","Ready to apply":"ÐоÑово к пÑименениÑ","Noise:":"ШÑм:","Volume:":"ÐÑомкоÑÑÑ:","Peaks:":"Ðики:",Raw:"ÐÑÑ
однÑй",Cleaned:"ÐÑиÑеннÑй",Retake:"ÐовÑоÑиÑÑ","Apply Mic Check":"ÐÑимениÑÑ Ð¿ÑовеÑкÑ","Mic is too loud":"ÐикÑоÑон звÑÑÐ¸Ñ ÑлиÑком гÑомко","Background noise is high":"ÐÑÑокий ÑоновÑй ÑÑм","Voice is quiet":"ÐÐ¾Ð»Ð¾Ñ Ð·Ð²ÑÑÐ¸Ñ ÑиÑ
о","Voice is loud":"ÐÐ¾Ð»Ð¾Ñ Ð·Ð²ÑÑÐ¸Ñ Ð³Ñомко","Mic sounds ready":"ÐикÑоÑон наÑÑÑоен","Start Mic Check":"ÐаÑаÑÑ Ð¿ÑовеÑкÑ","Connecting Mic...":"ÐодклÑÑение микÑоÑона...","Stop Recording":"ÐÑÑановиÑÑ Ð·Ð°Ð¿Ð¸ÑÑ",Record:"ÐапиÑаÑÑ","Retry Mic":"ÐовÑоÑиÑÑ Ð¿Ð¾Ð´ÐºÐ»ÑÑение",Replay:"ÐовÑоÑ","My Take":"ÐÐ¾Ñ Ð·Ð°Ð¿Ð¸ÑÑ",Retry:"ÐапиÑаÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾",Next:"Ðалее",Finish:"ÐавеÑÑиÑÑ",Skip:"ÐÑопÑÑÑиÑÑ","GET READY":"ÐÐ ÐÐÐТÐÐЬТÐСЬ",RECORDING:"ÐÐÐÐСЬ","CLIP {current} / {total}":"ÐÐÐÐ {current} / {total}","Jump ahead to another clip":"ÐеÑейÑи к дÑÑÐ³Ð¾Ð¼Ñ ÐºÐ»Ð¸Ð¿Ñ","Replay original clip":"ÐовÑоÑиÑÑ Ð¾ÑигиналÑнÑй клип","Finish pack":"ÐавеÑÑиÑÑ Ð¼Ð¾Ð´","Go to next clip":"ÐеÑейÑи к ÑледÑÑÑÐµÐ¼Ñ ÐºÐ»Ð¸Ð¿Ñ",Start:"ÐаÑаÑÑ","Starting...":"ÐапÑÑк...","Could not start this mod":"Ðе ÑдалоÑÑ Ð·Ð°Ð¿ÑÑÑиÑÑ ÑÑÐ¾Ñ Ð¼Ð¾Ð´","Copy start error details":"ÐопиÑоваÑÑ ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾Ð± оÑибке","Creator:":"ÐвÑоÑ:","Characters:":"ÐеÑÑонажи:","No Dub Mods Yet":"Ðодов озвÑÑки пока неÑ","Subscribe to a pack from the Workshop, then start your game.":"ÐодпиÑиÑеÑÑ Ð½Ð° мод в ÐаÑÑеÑÑкой, заÑем наÑниÑе игÑÑ.","Browse Workshop":"ÐÑкÑÑÑÑ ÐаÑÑеÑÑкÑÑ","My Mods":"Ðои модÑ","Refresh Mods":"ÐбновиÑÑ Ð¼Ð¾Ð´Ñ","Refreshing Mods...":"Ðбновление модов...","Refreshing...":"Ðбновление...","Mods are up to date.":"ÐÐ¾Ð´Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ñ.","{count} mods":"Ðодов: {count}","{count} / syncing Workshop":"{count} / ÑинÑ
ÑонизаÑÐ¸Ñ Ñ ÐаÑÑеÑÑкой","{count} / {name} judges in use":"{count} / иÑполÑзÑеÑÑÑ Ð¶ÑÑи «{name}»","{count} / default judges in use":"{count} / иÑполÑзÑеÑÑÑ ÑÑандаÑÑное жÑÑи","Local mod import options":"ÐаÑамеÑÑÑ Ð¸Ð¼Ð¿Ð¾ÑÑа локалÑнÑÑ
модов","Import local file":"ÐмпоÑÑ Ð»Ð¾ÐºÐ°Ð»Ñного Ñайла","Dub Mod ZIP":"ZIP мода озвÑÑки","Dub Mod Folder":"Ðапка мода озвÑÑки","Judge Mod ZIP":"ZIP мода жÑÑи","Search by mod, creator, character, or type":"ÐоиÑк по модÑ, авÑоÑÑ, пеÑÑÐ¾Ð½Ð°Ð¶Ñ Ð¸Ð»Ð¸ ÑипÑ","Search mods":"ÐоиÑк модов","Clear search":"ÐÑиÑÑиÑÑ Ð¿Ð¾Ð¸Ñк","Syncing Workshop subscriptions":"СинÑ
ÑонизаÑÐ¸Ñ Ð¿Ð¾Ð´Ð¿Ð¸Ñок ÐаÑÑеÑÑкой","Checking which installed mods belong to your account.":"ÐÑовеÑÑем, какие ÑÑÑановленнÑе Ð¼Ð¾Ð´Ñ ÑвÑÐ·Ð°Ð½Ñ Ñ Ð²Ð°Ñим аккаÑнÑом.","No mods yet":"Ðодов пока неÑ","Subscribe to a pack from the Workshop, then start a game in":"ÐодпиÑиÑеÑÑ Ð½Ð° мод в ÐаÑÑеÑÑкой, заÑем наÑниÑе игÑÑ Ð² Ñежиме",Solo:"ÐдиноÑÐ½Ð°Ñ Ð¸Ð³Ñа",or:"или"," game.":".",'No mods match "{query}"':"ÐÐµÑ Ð¼Ð¾Ð´Ð¾Ð² по запÑоÑÑ Â«{query}»","Clear Search":"ÐÑиÑÑиÑÑ Ð¿Ð¾Ð¸Ñк","View installation progress in Mods":"ÐоÑмоÑÑеÑÑ Ñ
од ÑÑÑановки в Ñазделе «ÐодÑ»","Vie
2w in Mods":"ÐÑкÑÑÑÑ Ð² модаÑ
","Show clip scores":"ÐоказаÑÑ Ð¾Ñенки клипов","Reconnect microphone and record":"ÐеÑеподклÑÑиÑÑ Ð¼Ð¸ÐºÑоÑон и запиÑаÑÑ","Play your recorded take with background audio":"ÐоÑпÑоизвеÑÑи ÑÐ²Ð¾Ñ Ð·Ð°Ð¿Ð¸ÑÑ Ñ ÑоновÑм звÑком","Play your recorded take":"ÐоÑпÑоизвеÑÑи ÑÐ²Ð¾Ñ Ð·Ð°Ð¿Ð¸ÑÑ","Background volume":"ÐÑомкоÑÑÑ Ñона",Background:"Фон",Cancel:"ÐÑмена","Report Player":"ÐожаловаÑÑÑÑ Ð½Ð° игÑока","Mod feedback":"ÐÑзÑв о моде","Report this mod":"ÐожаловаÑÑÑÑ Ð½Ð° мод","Report reason":"ÐÑиÑина жалобÑ","Incorrect role information":"ÐевеÑÐ½Ð°Ñ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾ ÑолÑÑ
","Incorrect language":"ÐевеÑнÑй ÑзÑк","Broken or misleading mod":"ÐеиÑпÑавнÑй или вводÑÑий в заблÑждение мод","Inappropriate content":"ÐеподÑ
одÑÑий конÑенÑ","Copyright or rights issue":"ÐаÑÑÑение авÑоÑÑкиÑ
или дÑÑгиÑ
пÑав","Spam or abuse":"Спам или злоÑпоÑÑебление","Describe the problem":"ÐпиÑиÑе пÑоблемÑ","Send Report":"ÐÑпÑавиÑÑ Ð¶Ð°Ð»Ð¾Ð±Ñ","Sending...":"ÐÑпÑавка...","Report received.":"Ðалоба полÑÑена.","Report could not be submitted.":"Ðе ÑдалоÑÑ Ð¾ÑпÑавиÑÑ Ð¶Ð°Ð»Ð¾Ð±Ñ.","Close report dialog":"ÐакÑÑÑÑ Ð¾ÐºÐ½Ð¾ жалобÑ","Reported Player":"ÐгÑок","Report Type":"Тип наÑÑÑениÑ","Choose a violation type":"ÐÑбеÑиÑе Ñип наÑÑÑениÑ",Description:"ÐпиÑание","Describe what happened so moderators can review the report.":"ÐпиÑиÑе, ÑÑо пÑоизоÑло, ÑÑÐ¾Ð±Ñ Ð¼Ð¾Ð´ÐµÑаÑоÑÑ Ð¼Ð¾Ð³Ð»Ð¸ ÑаÑÑмоÑÑеÑÑ Ð¶Ð°Ð»Ð¾Ð±Ñ.","At least 10 characters.":"Ðе менее 10 Ñимволов.","Submit Report":"ÐÑпÑавиÑÑ Ð¶Ð°Ð»Ð¾Ð±Ñ","Harassment / Abuse":"ÐомогаÑелÑÑÑва / оÑкоÑблениÑ","Hate Speech":"ЯзÑк вÑаждÑ",Spam:"Спам","Inappropriate Content":"ÐепÑиемлемÑй конÑенÑ",Other:"ÐÑÑгое","Clip Scores":"ÐÑенки клипов","{completed} / {total} clips completed":"ÐавеÑÑено клипов: {completed} / {total}",Detail:"ÐодÑобнее","Back to final score":"ÐеÑнÑÑÑÑÑ Ðº иÑÐ¾Ð³Ð¾Ð²Ð¾Ð¼Ñ ÑÑÑÑÑ","No playable dub video is available.":"Ðидео озвÑÑки недоÑÑÑпно Ð´Ð»Ñ Ð²Ð¾ÑпÑоизведениÑ.","Play my dubbed work":"ÐоÑпÑоизвеÑÑи Ð¼Ð¾Ñ Ð¾Ð·Ð²ÑÑкÑ","PUBLIC MULTIPLAYER":"ÐУÐÐÐЧÐÐЯ ÐÐÐ Ð","Public Lobby":"ÐÑблиÑное лобби","Refresh public rooms":"ÐбновиÑÑ Ð¿ÑблиÑнÑе комнаÑÑ","Multiplayer Lobby":"СеÑевое лобби","Refresh rooms":"ÐбновиÑÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","All Languages":"ÐÑе ÑзÑки",Status:"СÑаÑÑÑ","All Rooms":"ÐÑе комнаÑÑ","Waiting Only":"ТолÑко ожидаÑÑие","In Progress":"ÐдÑÑ Ð¸Ð³Ñа",Results:"РезÑлÑÑаÑÑ",Waiting:"Ðжидание","No public rooms match these filters.":"ÐÐµÑ Ð¿ÑблиÑнÑÑ
ÐºÐ¾Ð¼Ð½Ð°Ñ Ñ Ñакими ÑилÑÑÑами.","Loading public rooms...":"ÐагÑÑзка пÑблиÑнÑÑ
комнаÑ...","No rooms match these filters.":"ÐÐµÑ ÐºÐ¾Ð¼Ð½Ð°Ñ Ñ Ñакими ÑилÑÑÑами.","Loading rooms...":"ÐагÑÑзка комнаÑ...","Room visibility":"ÐидимоÑÑÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ",Private:"ÐÑиваÑнаÑ","Private Room":"ÐÑиваÑÐ½Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°Ñа",Public:"ÐÑблиÑнаÑ","Room Language":"ЯзÑк комнаÑÑ","Sign in to create a public room":"ÐойдиÑе, ÑÑÐ¾Ð±Ñ ÑоздаÑÑ Ð¿ÑблиÑнÑÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","Waiting for host to choose a mod":"Ðжидание вÑбоÑа мода Ñ
оÑÑом",Join:"ÐойÑи","View Profile":"ÐÑкÑÑÑÑ Ð¿ÑоÑилÑ","Host must start: {count}s":"ХоÑÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ наÑаÑÑ: {count} Ñ","Room Chat":"Ð§Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","No messages yet.":"СообÑений пока неÑ.","Message the room":"ÐапиÑаÑÑ Ð² комнаÑÑ","Reconnecting chat...":"ÐеÑеподклÑÑение ÑаÑа...","Send message":"ÐÑпÑавиÑÑ ÑообÑение","Open room chat":"ÐÑкÑÑÑÑ ÑÐ°Ñ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","Close room chat":"ÐакÑÑÑÑ ÑÐ°Ñ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","Players online":"ÐгÑоки онлайн","{count} unread messages":"ÐепÑоÑиÑаннÑÑ
ÑообÑений: {count}","{name} disconnected.":"{name}: Ñоединение поÑеÑÑно.","{name} reconnected.":"{name} Ñнова в ÑеÑи.","{name} left the room.":"{name} Ð¿Ð¾ÐºÐ¸Ð´Ð°ÐµÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ.","This public room has been waiting for 50 minutes.":"ÐÑа пÑблиÑÐ½Ð°Ñ ÐºÐ¾Ð¼Ð½Ð°Ñа Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ Ñже 50 минÑÑ.","Player Name":"ÐÐ¼Ñ Ð¸Ð³Ñока","Create Room":"СоздаÑÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","Creating...":"Создание...","Room Code":"Ðод комнаÑÑ","Join Room":"ÐойÑи в комнаÑÑ","Joining...":"ÐодклÑÑение...","Leave Room":"Ðокин
2ÑÑÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ","ROOM CODE":"ÐÐÐ ÐÐÐÐÐТЫ","Copy Code":"ÐопиÑоваÑÑ ÐºÐ¾Ð´","Copy Invite Link":"ÐопиÑоваÑÑ ÑÑÑлкÑ",Copied:"СкопиÑовано",Host:"ХоÑÑ",Ready:"ÐоÑов",Rendering:"СоздаÑÑÑÑ","Not rendered":"Ðе Ñоздано","Waiting â click to switch rendering":"Ðжидание â нажмиÑе, ÑÑÐ¾Ð±Ñ Ð¿ÐµÑеклÑÑиÑÑ Ð¾Ð±ÑабоÑкÑ","Not Ready":"Ðе гоÑов","Kick Player":"ÐÑклÑÑиÑÑ Ð¸Ð³Ñока","Host actions for {name}":"ÐейÑÑÐ²Ð¸Ñ Ñ
оÑÑа Ð´Ð»Ñ {name}","Empty player slot":"Свободное меÑÑо игÑока","Ready Up":"ÐоÑов","Cancel Ready":"ÐÑмениÑÑ Ð³Ð¾ÑовноÑÑÑ","Installing room mod":"УÑÑановка мода комнаÑÑ","Installing room mod {percent}%":"УÑÑановка мода комнаÑÑ: {percent}%","Start Game":"ÐаÑаÑÑ Ð¸Ð³ÑÑ","Starting in {count}":"ÐаÑало ÑеÑез {count}","How multiplayer mods sync":"Ðак ÑинÑ
ÑонизиÑÑÑÑÑÑ ÑеÑевÑе модÑ","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"ÐодпиÑки ÐаÑÑеÑÑкой ÑÑÑанавливаÑÑÑÑ Ð°Ð²ÑомаÑиÑеÑки Ð´Ð»Ñ Ð²Ð¾ÑедÑиÑ
игÑоков. ÐокалÑнÑе Ð¼Ð¾Ð´Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ ÑкаÑаÑÑ Ð¸ импоÑÑиÑоваÑÑ ÐºÐ°Ð¶Ð´Ñй ÑÑаÑÑник комнаÑÑ.","Change Mod":"СмениÑÑ Ð¼Ð¾Ð´","Choose a Mod":"ÐÑбеÑиÑе мод","Import a mod first":"СнаÑала импоÑÑиÑÑйÑе мод","Multiplayer mode":"Режим ÑеÑевой игÑÑ","Co-op":"СовмеÑÑно","Rotate turns or let players claim open clips.":"ÐеÑедавайÑе Ñ
од по оÑеÑеди или ÑазÑеÑиÑе игÑокам вÑбиÑаÑÑ ÑвободнÑе клипÑ.","Everyone records the same clips and compares scores.":"ÐÑе запиÑÑваÑÑ Ð¾Ð´Ð¸Ð½Ð°ÐºÐ¾Ð²Ñе ÐºÐ»Ð¸Ð¿Ñ Ð¸ ÑÑавниваÑÑ ÑезÑлÑÑаÑÑ.","Co-op Turn Mode":"ÐоÑÑдок Ñ
одов в ÑовмеÑÑной игÑе","Co-op Assignment":"РаÑпÑеделение Ñолей и Ñ
одов","Rotate turns, claim clips, or claim roles.":"ÐгÑайÑе по оÑеÑеди или вÑбиÑайÑе ÐºÐ»Ð¸Ð¿Ñ Ð¸ Ñоли.","Round Robin":"Ðо оÑеÑеди","Claim Mode":"Ð
2ÑÐ±Ð¾Ñ ÐºÐ»Ð¸Ð¿Ð°","Role Claim":"ÐÑÐ±Ð¾Ñ Ñоли","Co-op · Role Claim":"СовмеÑÑÐ½Ð°Ñ Ð¸Ð³Ñа · ÐÑÐ±Ð¾Ñ Ñолей","Choose Roles":"ÐÑбÑаÑÑ Ñоли","Back to room":"ÐеÑнÑÑÑÑÑ Ð² комнаÑÑ","All roles are assigned.":"ÐÑе Ñоли ÑаÑпÑеделенÑ.","Start Dubbing":"ÐаÑаÑÑ Ð¾Ð·Ð²ÑÑкÑ","Waiting for the host to start dubbing.":"Ðжидание наÑала озвÑÑки Ð¾Ñ Ð²ÐµÐ´ÑÑего.","Syncing role selection":"СинÑ
ÑонизаÑÐ¸Ñ Ñолей","This mod has no roles to claim":"Ð ÑÑом моде Ð½ÐµÑ Ñолей Ð´Ð»Ñ Ð²ÑбоÑа","Claim every role before starting":"ÐеÑед наÑалом ÑаÑпÑеделиÑе вÑе Ñоли","Choose your roles":"ÐÑбеÑиÑе Ñоли","Choose your roles.":"ÐÑбеÑиÑе Ñоли.",Claim:"ÐанÑÑÑ ÑолÑ",Release:"ÐÑвободиÑÑ ÑолÑ","Returned to lobby.":"ÐозвÑÐ°Ñ Ð² комнаÑÑ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ.","Could not start role selection.":"Ðе ÑдалоÑÑ Ð½Ð°ÑаÑÑ Ð²ÑÐ±Ð¾Ñ Ñолей.","Could not return to lobby.":"Ðе ÑдалоÑÑ Ð²ÐµÑнÑÑÑÑÑ Ð² комнаÑÑ Ð¾Ð¶Ð¸Ð´Ð°Ð½Ð¸Ñ.","Could not update role.":"Ðе ÑдалоÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð¸ÑÑ ÑолÑ.","Session complete":"РаÑнд завеÑÑÑн","Click a role to claim it. Click again to release it.":"ÐажмиÑе на ÑолÑ, ÑÑÐ¾Ð±Ñ Ð·Ð°Ð½ÑÑÑ ÐµÑ. ÐажмиÑе еÑÑ Ñаз, ÑÑÐ¾Ð±Ñ Ð¾ÑвободиÑÑ.","Preview lines":"ÐÑоÑмоÑÑеÑÑ Ñеплики","Loading lines...":"ÐагÑÑзка Ñеплик...","LINE PREVIEW":"ÐÐ ÐСÐÐТРРÐÐÐÐÐ","{count} lines":"Реплик: {count}","Filter lines by role":"ФилÑÑÑ Ñеплик по Ñоли",All:"ÐÑе",Unassigned:"Ðез Ñоли","No lines found for this role.":"ÐÐ»Ñ ÑÑой Ñоли Ñеплики не найденÑ.",claimed:"занÑÑо","Claim a role before the host starts. You can take more than one.":"ÐÑбеÑиÑе ÑÐ¾Ð»Ñ Ð´Ð¾ наÑала игÑÑ. Ðожно взÑÑÑ Ð½ÐµÑколÑко Ñолей.","Randomize Player Order":"СлÑÑайнÑй поÑÑдок игÑоков","Shuffles the player order once when the game starts, then continues rotating fairly.":"ÐеÑемеÑÐ¸Ð²Ð°ÐµÑ Ð¸Ð³Ñоков один Ñаз пÑи запÑÑке, поÑле Ñего Ñ
Ð¾Ð´Ñ Ð¿ÑодолжаÑÑÑÑ Ð¿Ð¾ кÑÑгÑ.","PVP Format":"ФоÑÐ¼Ð°Ñ PVP","Turn Battle":"ÐиÑва по Ñ
одам","Parallel Race":"ÐаÑаллелÑÐ½Ð°Ñ Ð³Ð¾Ð½ÐºÐ°","Players record separately. Faster players get a higher score.":"ÐгÑоки запиÑÑваÑÑÑÑ Ð¾ÑделÑно. Ðолее бÑÑÑÑÑе полÑÑаÑÑ Ð±Ð¾Ð»ÑÑе оÑков.","Players take turns recording the same clip before moving to the next one.":"ÐгÑоки по оÑеÑеди запиÑÑваÑÑ Ð¾Ð´Ð¸Ð½ клип, заÑем пеÑеÑ
одÑÑ Ðº ÑледÑÑÑемÑ.","Allow Re-records":"РазÑеÑиÑÑ Ð¿ÐµÑезапиÑÑ","Player Limit":"ÐÐ¸Ð¼Ð¸Ñ Ð¸Ð³Ñоков",Confirm:"ÐодÑвеÑдиÑÑ","On: players can clear their current multiplayer take and record it again before submitting.":"ÐгÑоки могÑÑ ÑдалиÑÑ ÑекÑÑÑÑ ÑеÑевÑÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¸ запиÑаÑÑ ÐµÑ Ð·Ð°Ð½Ð¾Ð²Ð¾ пеÑед оÑпÑавкой.","DUB MOD":"ÐÐÐ ÐÐÐУЧÐÐ","{count} players in room":"ÐгÑоков в комнаÑе: {count}","Score Judging":"ÐодÑÑÑÑ Ð¾Ñенки","Judge feedback":"ÐÑзÑÐ²Ñ ÑÑдей","Judge score stage":"СÑена ÑÑдейÑкиÑ
оÑенок","Preparing score":"ÐодгоÑовка оÑенки","Your {label} is":"ÐоказаÑÐµÐ»Ñ Â«{label}»:","Final score {score}":"ÐÑоговÑй ÑÑÑÑ: {score}",pending:"ожидание",Continue:"ÐÑодолжиÑÑ",Duration:"ÐлиÑелÑноÑÑÑ",Timing:"Тайминг",Energy:"ÐнеÑгиÑ",Pitch:"ÐÑÑоÑа Ñона","Perfect Fit":"ÐдеалÑно","Good Fit":"ХоÑоÑо",Uneven:"ÐеÑовно","Too Short/Long":"СлиÑком коÑоÑко/долго","Locked In":"ТоÑно в ÑиÑм","On Time":"ÐовÑемÑ",Close:"ÐакÑÑÑÑ","Off Beat":"Ðимо ÑиÑма","Stage Ready":"ÐоÑово Ð´Ð»Ñ ÑÑенÑ",Expressive:"ÐÑÑазиÑелÑно",Solid:"УвеÑенно",Flat:"Ðез ÑмоÑий",Uncanny:"ÐоÑазиÑелÑно","Very Close"
2:"ÐÑÐµÐ½Ñ Ð±Ð»Ð¸Ð·ÐºÐ¾","Close Enough":"ÐоÑÑаÑоÑно близко",Different:"ÐÑлиÑаеÑÑÑ","Perfect Dub":"ÐдеалÑÐ½Ð°Ñ Ð¾Ð·Ð²ÑÑка","Viral Performance":"ÐиÑÑÑное вÑÑÑÑпление","Scene Stealer":"Ðвезда ÑÑенÑ","Great Take":"ÐÑлиÑнÑй дÑблÑ","Solid Dub":"ХоÑоÑÐ°Ñ Ð¾Ð·Ð²ÑÑка","Getting There":"Уже неплоÑ
о","Keep Practicing":"ÐÑодолжайÑе ÑÑениÑоваÑÑÑÑ","Download exports":"СкаÑаÑÑ ÑезÑлÑÑаÑÑ","Download My Work":"СкаÑаÑÑ Ð¼Ð¾Ñ ÑабоÑÑ","Your browser needs one more tap to play this mod video.":"ÐажмиÑе еÑÑ Ñаз, ÑÑÐ¾Ð±Ñ Ð±ÑаÑÐ·ÐµÑ Ð²Ð¾ÑпÑоизвÑл видео мода.","Continue Export":"ÐÑодолжиÑÑ ÑкÑпоÑÑ","Video subtitle option":"ÐаÑÑÑойка ÑÑбÑиÑÑов видео",Clean:"Ðез ÑÑбÑиÑÑов",Subtitles:"СÑбÑиÑÑÑ","Download my vertical Shorts video":"СкаÑаÑÑ Ð²ÐµÑÑикалÑное видео Shorts","Save Shorts":"СоÑ
ÑаниÑÑ Shorts","My Shorts":"Ðои Shorts",Video:"Ðидео","Download my landscape video":"СкаÑаÑÑ Ð³Ð¾ÑизонÑалÑное видео","Save Landscape":"СоÑ
ÑаниÑÑ Ð²Ð¸Ð´ÐµÐ¾","My Landscape":"ÐÐ¾Ñ Ð³Ð¾ÑизонÑалÑное","Download my audio track":"СкаÑаÑÑ Ð°ÑдиодоÑожкÑ","Save Audio":"СоÑ
ÑаниÑÑ Ð°Ñдио","My Audio":"ÐÐ¾Ñ Ð°Ñдио",Track:"ÐоÑожка","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Ðе закÑÑвайÑе ÑÑÑаниÑÑ Ð²Ð¾ вÑÐµÐ¼Ñ Ð¿Ð¾Ð´Ð³Ð¾Ñовки. ÐлиннÑе видео могÑÑ Ð¾Ð±ÑабаÑÑваÑÑÑÑ Ð½ÐµÑколÑко минÑÑ. ÐоÑле завеÑÑÐµÐ½Ð¸Ñ Ð½Ð°Ð¶Ð¼Ð¸Ñе «СоÑ
ÑаниÑÑ», ÑÑÐ¾Ð±Ñ Ð¾ÑкÑÑÑÑ Ð¼ÐµÐ½Ñ iPhone.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"ÐкÑпоÑÑ Ð²ÑполнÑеÑÑÑ Ð² WebM. ÐÐ»Ñ Ð²Ð¾ÑпÑÐ¾Ð¸Ð·Ð²ÐµÐ´ÐµÐ½Ð¸Ñ ÑекомендÑеÑÑÑ Chrome; еÑли нÑжен MP4, пÑеобÑазÑйÑе Ñайл ÑамоÑÑоÑÑелÑно.","Share exports":"ÐоделиÑÑÑÑ ÑезÑлÑÑаÑами",Share:"ÐоделиÑÑÑÑ","Copy share caption":"ÐопиÑоваÑÑ ÑекÑÑ Ð¿ÑбликаÑии","Copy caption":"ÐопиÑоваÑÑ ÑекÑÑ","Rotate your phone":"ÐовеÑниÑе ÑелеÑон","Landscape gives the video and recording controls more room.":"РалÑбомной оÑиенÑаÑии болÑÑе меÑÑа Ð´Ð»Ñ Ð²Ð¸Ð´ÐµÐ¾ и ÑпÑÐ°Ð²Ð»ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ð¸ÑÑÑ.","Stay in portrait":"ÐÑÑаÑÑÑÑ Ð² поÑÑÑеÑном Ñежиме","Previous run ended unexpectedly":"ÐÑедÑдÑÑÐ°Ñ Ð¸Ð³Ñа неожиданно завеÑÑилаÑÑ","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike пеÑезагÑÑзилÑÑ Ð²Ð¾ вÑÐµÐ¼Ñ Ð¸Ð³ÑÑ. СкопиÑÑйÑе ÑÑÐ¾Ñ Ð¾ÑÑÑÑ, еÑли Ñбой повÑоÑиÑÑÑ.","Copy crash report":"СкопиÑоваÑÑ Ð¾ÑÑÑÑ Ð¾ Ñбое","Dismiss crash report":"ÐакÑÑÑÑ Ð¾ÑÑÑÑ Ð¾ Ñбое","Page interrupted":"РабоÑа ÑÑÑаниÑÑ Ð¿ÑеÑвана","Your room and locally saved recordings can be restored after reloading.":"ÐоÑле пеÑезагÑÑзки можно воÑÑÑановиÑÑ ÐºÐ¾Ð¼Ð½Ð°ÑÑ Ð¸ локалÑнÑе запиÑи.","Reload Voxalike":"ÐеÑезагÑÑзиÑÑ Voxalike","ROOM CLOSING SOON":"ÐÐÐÐÐТРСÐÐÐ Ð ÐÐÐÐ ÐÐТСЯ","This room has been waiting for 10 minutes.":"ÐÑа комнаÑа Ð¾Ð¶Ð¸Ð´Ð°ÐµÑ Ñже 10 минÑÑ.","Start the game within the next 10 minutes or the room will close automatically.":"ÐаÑниÑе игÑÑ Ð² ÑеÑение ÑледÑÑÑиÑ
10 минÑÑ, инаÑе комнаÑа закÑоеÑÑÑ Ð°Ð²ÑомаÑиÑеÑки.","Room closed because no game started within 20 minutes.":"ÐомнаÑа закÑÑÑа, поÑÐ¾Ð¼Ñ ÑÑо игÑа не наÑалаÑÑ Ð² ÑеÑение 20 минÑÑ.","Room closed because the host did n
2ot reconnect within 5 minutes.":"ÐомнаÑа закÑÑÑа, поÑÐ¾Ð¼Ñ ÑÑо ведÑÑий не подклÑÑилÑÑ Ñнова в ÑеÑение 5 минÑÑ.","Wait for disconnected players to reconnect":"ÐождиÑеÑÑ Ð¿Ð¾Ð²ÑоÑного подклÑÑÐµÐ½Ð¸Ñ Ð¾ÑÑоединивÑиÑ
ÑÑ Ð¸Ð³Ñоков",Reconnecting:"ÐеÑеподклÑÑение","Game server updated":"ÐгÑовой ÑеÑÐ²ÐµÑ Ð¾Ð±Ð½Ð¾Ð²Ð»Ñн","Connection interrupted":"Соединение пÑеÑвано","Syncing the latest game server and restoring your room.":"СинÑ
ÑонизиÑÑем поÑледнÑÑ Ð²ÐµÑÑÐ¸Ñ Ð¸Ð³Ñового ÑеÑвеÑа и воÑÑÑанавливаем комнаÑÑ.","Your connection dropped. Trying to reconnect to your room.":"Соединение поÑеÑÑно. ÐÑÑаемÑÑ Ñнова подклÑÑиÑÑÑÑ Ðº комнаÑе."},uk:{"Interface Language":"Ðова ÑнÑеÑÑейÑÑ","Choose the language used by the game interface.":"ÐибеÑÑÑÑ Ð¼Ð¾Ð²Ñ ÑгÑового ÑнÑеÑÑейÑÑ.",Language:"Ðова",PLAY:"ÐÐ ÐТÐ",NEWS:"ÐÐÐÐÐÐ","DEVELOPER NEWS":"ÐÐÐÐÐÐ Ð ÐÐÐ ÐÐÐÐÐÐÐ","Voxalike News":"Ðовини Voxalike",GUIDE:"ÐÐСÐÐÐÐÐ",WORKSHOP:"ÐÐÐСТÐÐ ÐЯ",SUPPORTER:"СÐÐÐСÐÐ ",SUPPORT:"ÐÐÐТРÐÐÐТÐ","Sign in":"УвÑйÑи","Account menu":"ÐÐµÐ½Ñ Ð¾Ð±Ð»Ñкового запиÑÑ",Notifications:"СповÑÑеннÑ",Profile:"ÐÑоÑÑлÑ","Account details":"ÐÐ°Ð½Ñ Ð¾Ð±Ð»Ñкового запиÑÑ","Edit Profile":"РедагÑваÑи пÑоÑÑлÑ",Studio:"СÑÑдÑÑ","Support Again":"ÐÑдÑÑимаÑи зновÑ","Become a Supporter":"СÑаÑи ÑпонÑоÑом","Support Voxalike":"ÐÑдÑÑимаÑи Voxalike","Voxalike Player":"ÐÑавеÑÑ Voxalike","Supporter profile preview":"ÐопеÑеднÑй пеÑеглÑд пÑоÑÑÐ»Ñ Supporter","Profile preview":"ÐопеÑеднÑй пеÑеглÑд пÑоÑÑлÑ","6 name colors":"6 колÑоÑÑв ÑменÑ","3 avatar frames":"3 Ñамки аваÑаÑа","4 avatar frames":"4 Ñамки аваÑаÑа","3 avatar frames + 1 animated bonus frame":"3 Ñамки аваÑаÑа + 1 бонÑÑна анÑмована Ñамка","Try on {frame}":"ÐÑимÑÑÑÑи {frame}","Supporter purchase":"ÐÑпÑÐ²Ð»Ñ Supporter","one time":"одноÑазово","No subscription":"Ðез пеÑедплаÑи","Animated Supporter badge":"ÐнÑмований знаÑок Supporter","Checkout could not start. Please try again.":"Ðе вдалоÑÑ ÑозпоÑаÑи оплаÑÑ. СпÑобÑйÑе Ñе Ñаз.","Checkout was canceled. Nothing was charged.":"ÐплаÑÑ ÑкаÑовано. ÐоÑÑи не ÑпиÑано.","Payment is being confirmed. Your benefits will appear here automatically.":"ÐлаÑÑж пÑдÑвеÑджÑÑÑÑÑÑ. ÐаÑÑ Ð¿ÐµÑеваги зâÑвлÑÑÑÑÑ ÑÑÑ Ð°Ð²ÑомаÑиÑно.","Buy Supporter for US$5":"ÐÑпиÑи Supporter за US$5","Gift code checkout is not available yet.":"ÐÑпÑÐ²Ð»Ñ Ð¿Ð¾Ð´Ð°ÑÑнкового ÐºÐ¾Ð´Ñ Ð¿Ð¾ÐºÐ¸ недоÑÑÑпна.","Payment is being confirmed. Your gift code will appear here automatically.":"ÐлаÑÑж пÑдÑвеÑджÑÑÑÑÑÑ. ÐодаÑÑнковий код зâÑвиÑÑÑÑ ÑÑÑ Ð°Ð²ÑомаÑиÑно.","Buy Supporter or a gift code":"ÐÑпиÑи Supporter або подаÑÑнковий код","Activate on this account":"ÐкÑивÑваÑи в ÑÑÐ¾Ð¼Ñ Ð¾Ð±Ð»ÑÐºÐ¾Ð²Ð¾Ð¼Ñ Ð·Ð°Ð¿Ð¸ÑÑ","Supporter benefits start after payment.":"ÐеÑеваги Supporter акÑивÑÑÑÑÑÑ Ð¿ÑÑÐ»Ñ Ð¾Ð¿Ð»Ð°Ñи.","Buy a Supporter gift code":"ÐÑпиÑи подаÑÑнковий код Supporter","Give Supporter":"ÐодаÑÑваÑи Supporter","Manage gift codes":"ÐеÑÑваÑи подаÑÑнковими кодами","SUPPORTER GIFT":"ÐÐÐÐРУÐÐÐ SUPPORTER","Gift delivery method":"СпоÑÑб доÑÑавки подаÑÑнка","Send to a player":"ÐадÑÑлаÑи гÑавÑÑ","Delivered directly to their account":"ÐÑде додано безпоÑеÑеднÑо в акаÑнÑ","Buy gift codes":"ÐÑпиÑи подаÑÑÐ½ÐºÐ¾Ð²Ñ ÐºÐ¾Ð´Ð¸","Share the codes yourself":"ÐеÑедайÑе коди ÑамоÑÑÑйно","Player's Voxalike email":"ÐлекÑÑонна поÑÑа гÑавÑÑ Ñ Voxalike","Searching...":"ÐоÑÑк...","Find player":"ÐнайÑи гÑавÑÑ","Could not find that player.":"Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñи ÑÑого гÑавÑÑ.","Confirm recipient":"ÐÑдÑвеÑдÑÑе одеÑжÑваÑа","Open profile":"ÐÑдкÑиÑи пÑоÑÑлÑ","This is your account":"Це Ð²Ð°Ñ Ð°ÐºÐ°ÑнÑ","Already has Supporter":"Supporter вже акÑивний","Gift Supporter for US$5":"ÐодаÑÑваÑи Supporter за 5 $","Number of gift co
2des":"ÐÑлÑкÑÑÑÑ Ð¿Ð¾Ð´Ð°ÑÑнковиÑ
кодÑв",Total:"Разом","Buy {count} gift codes":"ÐÑпиÑи подаÑÑÐ½ÐºÐ¾Ð²Ñ ÐºÐ¾Ð´Ð¸: {count}","Buy 1 gift code":"ÐÑпиÑи 1 подаÑÑнковий код","SUPPORTER GIFTS":"ÐÐÐÐРУÐÐÐ SUPPORTER","{count} unused codes":"ÐевикоÑиÑÑаниÑ
кодÑв: {count}","1 unused code":"1 невикоÑиÑÑаний код","Copy all unused":"СкопÑÑваÑи вÑÑ Ð½ÐµÐ²Ð¸ÐºÐ¾ÑиÑÑанÑ","Download unused":"ÐаванÑажиÑи невикоÑиÑÑанÑ","Loading gift codes...":"ÐаванÑÐ°Ð¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð´Ð°ÑÑнковиÑ
кодÑв...","You do not have any gift codes yet.":"У Ð²Ð°Ñ Ñе Ð½ÐµÐ¼Ð°Ñ Ð¿Ð¾Ð´Ð°ÑÑнковиÑ
кодÑв.","Payment is being confirmed. Your gift codes will appear in the gift manager automatically.":"ÐлаÑÑж пÑдÑвеÑджÑÑÑÑÑÑ. ÐодаÑÑÐ½ÐºÐ¾Ð²Ñ ÐºÐ¾Ð´Ð¸ авÑомаÑиÑно зâÑвлÑÑÑÑÑ Ð² ÑоздÑÐ»Ñ Ð¿Ð¾Ð´Ð°ÑÑнкÑв.","Payment is being confirmed. The gift will be delivered to {name} automatically.":"ÐлаÑÑж пÑдÑвеÑджÑÑÑÑÑÑ. ÐодаÑÑнок бÑде авÑомаÑиÑно доÑÑавлено гÑавÑÑ {name}.","Supporter was delivered to {name}.":"Supporter доÑÑавлено гÑавÑÑ {name}.","the selected player":"обÑÐ°Ð½Ð¾Ð¼Ñ Ð³ÑавÑÑ","That player already has Supporter.":"Цей гÑавеÑÑ Ñже Ð¼Ð°Ñ Supporter.","Use Activate on this account to buy Supporter for yourself.":"Щоб пÑидбаÑи Supporter Ð´Ð»Ñ Ñебе, ÑкоÑиÑÑайÑеÑÑ ÐºÐ½Ð¾Ð¿ÐºÐ¾Ñ Ð°ÐºÑиваÑÑÑ Ð½Ð° ÑÑÐ¾Ð¼Ñ Ð°ÐºÐ°ÑнÑÑ.","That Voxalike account is no longer available.":"Цей акаÑÐ½Ñ Voxalike бÑлÑÑе недоÑÑÑпний.","Give the code to any player to redeem.":"ÐеÑедайÑе код бÑдÑ-ÑÐºÐ¾Ð¼Ñ Ð³ÑавÑÐµÐ²Ñ Ð´Ð»Ñ Ð°ÐºÑиваÑÑÑ.","Your gift codes":"ÐаÑÑ Ð¿Ð¾Ð´Ð°ÑÑÐ½ÐºÐ¾Ð²Ñ ÐºÐ¾Ð´Ð¸",Redeemed:"ÐкÑивовано","Ready to redeem":"ÐоÑовий до акÑиваÑÑÑ","Copy code":"ÐопÑÑваÑи код","Redeem Supporter code":"ÐкÑивÑваÑи код Supporter","Enter Supporter code":"ÐведÑÑÑ ÐºÐ¾Ð´ Supporter","Redeem code":"ÐкÑивÑваÑи код","Code redeemed. Supporter is now active.":"Ðод акÑивовано. Supporter ÑÐµÐ¿ÐµÑ Ð´Ð¾ÑÑÑпний.","This code is invalid or is not ready yet.":"Цей код недÑйÑний або Ñе не гоÑовий.","This code has already been redeemed.":"Цей код Ñже бÑло акÑивовано.","Too many redemption attempts. Please wait a few minutes.":"ÐабагаÑо ÑпÑоб акÑиваÑÑÑ. ÐаÑекайÑе кÑлÑка Ñ
вилин.","Customize style":"ÐалаÑÑÑваÑи ÑÑилÑ","Voxalike is operated by QHyralis, LLC, the payment recipient.":"Voxalike кеÑÑÑ QHyralis, LLC â оÑÑимÑÐ²Ð°Ñ Ð¿Ð»Ð°ÑежÑ.","One payment. Permanent benefits.":"Ðдин плаÑÑж. ÐоÑÑÑÐ¹Ð½Ñ Ð¿ÐµÑеваги.","One-time payment":"ÐдноÑазовий плаÑÑж","Permanent Supporter badge":"ÐоÑÑÑйний знаÑок ÑпонÑоÑа","Ad-free on this account":"Ðез Ñеклами Ð´Ð»Ñ ÑÑого облÑкового запиÑÑ","Xsolla could not start checkout. Please try again.":"Xsolla не вдалоÑÑ ÑозпоÑаÑи оплаÑÑ. СпÑобÑйÑе Ñе Ñаз.","Too many checkout attempts. Please wait a few minutes.":"ÐабагаÑо ÑпÑоб оплаÑи. ÐаÑекайÑе кÑлÑка Ñ
вилин.","Supporter checkout is not available yet.":"ÐплаÑа Supporter поки недоÑÑÑпна.","Supporter is already active on this account.":"Supporter Ñже акÑивний Ð´Ð»Ñ ÑÑого облÑкового запиÑÑ.","Payment is being confirmed. Refresh in a moment.":"ÐлаÑÑж пÑдÑвеÑджÑÑÑÑÑÑ. ÐновÑÑÑ ÑÑоÑÑÐ½ÐºÑ Ð·Ð° кÑлÑка ÑекÑнд.","Supporter active":"Supporter акÑивний","Your badge and ad-free access are permanently active.":"ÐÐ°Ñ Ð·Ð½Ð°Ñок Ñ Ð´Ð¾ÑÑÑп без Ñеклами акÑÐ¸Ð²Ð¾Ð²Ð°Ð½Ñ Ð½Ð°Ð·Ð°Ð²Ð¶Ð´Ð¸.","Loading account...":"ÐаванÑÐ°Ð¶ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñкового запиÑÑ...","Sign in to continue":"УвÑ
2йдÑÑÑ, Ñоб пÑодовжиÑи","Buy Now":"ÐÑпиÑи заÑаз","Payments are processed securely by Xsolla.":"ÐлаÑÐµÐ¶Ñ Ð±ÐµÐ·Ð¿ÐµÑно обÑоблÑÑÑÑÑÑ Xsolla.","One purchase per account.":"Ðдна покÑпка на облÑковий запиÑ.","By continuing, you agree to the":"ÐÑодовжÑÑÑи, ви пÑиймаÑÑе",Terms:"Умови",and:"Ñ","Privacy Policy":"ÐолÑÑÐ¸ÐºÑ ÐºÐ¾Ð½ÑÑденÑÑйноÑÑÑ","Admin Benefits Active":"ÐеÑеваги адмÑнÑÑÑÑаÑоÑа акÑивнÑ","Contributor Benefits Active":"ÐеÑеваги ÑÑаÑника акÑивнÑ","Apply for Contributor":"СÑаÑи ÑÑаÑником","Admin Active":"ÐдмÑнÑÑÑÑаÑÐ¾Ñ Ð°ÐºÑивний","Contributor Active":"УÑаÑник акÑивний","Contributor Program":"ÐÑогÑама ÑÑаÑникÑв","Creator Workshop":"ÐайÑÑеÑÐ½Ñ Ð°Ð²ÑоÑа","Sign out":"ÐийÑи",Community:"СпÑлÑноÑа","Main menu":"Ðоловне менÑ","Single Player":"ÐдиноÑна гÑа",Multiplayer:"ÐеÑежева гÑа",Mods:"Ðоди",Settings:"ÐалаÑÑÑваннÑ","Install App":"УÑÑановиÑи заÑÑоÑÑнок","Community Contributor applications":"ÐаÑвки до пÑогÑами ÑÑаÑникÑв ÑпÑлÑноÑи",Dismiss:"ÐакÑиÑи","Dismiss contributor notice":"ÐакÑиÑи ÑповÑÑÐµÐ½Ð½Ñ Ð¿Ñо пÑогÑÐ°Ð¼Ñ ÑÑаÑникÑв","Limited Alpha Event":"ÐодÑÑ Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð¾Ñ Ð°Ð»ÑÑа-веÑÑÑÑ","Community Contributor applications are open":"ÐÑдкÑиÑо пÑийом заÑвок вÑд ÑÑаÑникÑв ÑпÑлÑноÑи","Earn Ad-Free access and an exclusive multiplayer badge.":"ÐÑÑимайÑе доÑÑÑп без Ñеклами й екÑклÑзивний меÑежевий знаÑок.","Ends Sep 8, 2026 at 11:59 PM PT":"Ðо 8 веÑеÑÐ½Ñ 2026 Ñ., 23:59 PT","View details":"ÐокладнÑÑе",Back:"Ðазад",Menu:"ÐенÑ","New Run":"Ðова гÑа","SINGLE PLAYER":"ÐÐÐÐÐЧÐÐ ÐÐ Ð",MULTIPLAYER:"ÐÐÐ ÐÐÐÐÐ ÐÐ Ð","Preparing Stage":"ÐÑдгоÑовка ÑÑени","My Dub Premiere":"ÐÑемâÑÑа мого озвÑÑеннÑ","Judge Score":"ÐÑÑнка ÑÑддÑв","Final Score":"ÐÑдÑÑмковий ÑаÑ
Ñнок","Dub Stage":"СÑена озвÑÑеннÑ","Create or Join":"СÑвоÑиÑи або пÑиÑднаÑиÑÑ","Recent Dub":"ÐÐµÐ´Ð°Ð²Ð½Ñ Ð¾Ð·Ð²ÑÑеннÑ","PLAYER OPTIONS":"ÐÐÐÐШТУÐÐÐÐЯ ÐÐ ÐÐЦЯ","Press F11 for fullscreen.":"ÐаÑиÑнÑÑÑ F11 Ð´Ð»Ñ Ð¿Ð¾Ð²Ð½Ð¾ÐµÐºÑанного ÑежимÑ.","MOD MANAGER":"ÐÐРУÐÐÐÐЯ ÐÐÐÐÐÐ","Audio Settings":"ÐалаÑÑÑÐ²Ð°Ð½Ð½Ñ Ð·Ð²ÑкÑ","Control game audio levels.":"ÐалаÑÑÑйÑе гÑÑнÑÑÑÑ Ð·Ð²ÑÐºÑ Ð² гÑÑ.","Master Volume":"ÐагалÑна гÑÑнÑÑÑÑ","Sound Effects":"ÐвÑÐºÐ¾Ð²Ñ ÐµÑекÑи","Background Music":"Фонова мÑзика","Input Device":"ÐÑиÑÑÑÑй введеннÑ","Voice Processing":"ÐбÑобка голоÑÑ","Microphone Volume":"ÐÑÑнÑÑÑÑ Ð¼ÑкÑоÑона","Changes recording playback and exports. Scoring is unaffected.":"ÐмÑнÑÑ Ð³ÑÑнÑÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑ Ð¿Ñд ÑÐ°Ñ Ð²ÑдÑвоÑÐµÐ½Ð½Ñ Ñа екÑпоÑÑÑ. Ðа оÑÑÐ½ÐºÑ Ð½Ðµ впливаÑ.",Natural:"ÐÑиÑодно","Browser noise reduction":"ШÑмозаглÑÑÐµÐ½Ð½Ñ Ð±ÑаÑзеÑа","Clean voice":"ЧиÑÑий голоÑ","Best clarity for headphones and quiet rooms.":"ÐайкÑаÑа ÑÑÑкÑÑÑÑ Ð´Ð»Ñ Ð½Ð°Ð²ÑÑникÑв Ñ ÑиÑ
иÑ
пÑимÑÑенÑ.","Uses only your browser's built-in noise reduction.":"ÐикоÑиÑÑовÑÑ Ð»Ð¸Ñе вбÑдоване ÑÑмозаглÑÑÐµÐ½Ð½Ñ Ð±ÑаÑзеÑа.","No browser or Voxalike voice processing.":"Ðез обÑобки голоÑÑ Ð±ÑаÑзеÑом або Voxalike.","Raw microphone active":"ÐеобÑоблений мÑкÑоÑон ÑвÑмкнено","Browser and Voxalike voice processing are disabled; no calibration is needed.":"ÐбÑÐ¾Ð±ÐºÑ Ð³Ð¾Ð»Ð¾ÑÑ Ð±ÑаÑзеÑом Ñ Voxalike вимкнено; калÑбÑÑÐ²Ð°Ð½Ð½Ñ Ð½Ðµ поÑÑÑбне.","Record a short sample to hear your microphone without voice processing.":"ÐапиÑÑÑÑ ÐºÐ¾ÑоÑкий зÑазок, Ñоб поÑÑÑи мÑкÑоÑон без обÑобки голоÑÑ.","Listen to the sample to hear your microphone without voice processing.":"ÐÑоÑлÑÑ
айÑе зÑазок, Ñоб поÑÑÑи мÑкÑоÑон без обÑобки голоÑÑ.","Browser noise reduction active":"ШÑмозаглÑÑÐµÐ½Ð½Ñ Ð±ÑаÑзеÑа ввÑмкнено","Your browser handles noise reduction automatically; no calibration is needed.":"ÐÑаÑÐ·ÐµÑ Ð°Ð²ÑомаÑиÑно обÑоблÑÑ ÑÑм; калÑбÑÑÐ²Ð°Ð½Ð½Ñ Ð½Ðµ поÑÑÑбне.","Record a short sample to hear your browser's built-in noise reduction.":"ÐапиÑÑÑÑ ÐºÐ¾ÑоÑкий зÑазок, Ñоб пеÑевÑÑиÑи вбÑдоване ÑÑмозаглÑÑÐµÐ½Ð½Ñ Ð±ÑаÑзеÑа.","Listen to the sample to hear your browser's built-in noise reduction.":"ÐÑоÑлÑÑ
айÑе зÑазок, Ñоб оÑÑниÑи вбÑдоване ÑÑмозаглÑÑÐµÐ½Ð½Ñ Ð±ÑаÑзеÑа.","Play
2sample":"ÐÑдÑвоÑиÑи зÑазок","Stronger cleanup for fans, keyboards and noisy rooms.":"ÐоÑилене оÑиÑÐµÐ½Ð½Ñ Ð²Ñд венÑилÑÑоÑÑв, клавÑаÑÑÑи Ñа ÑÑÐ¼Ñ Ð² пÑимÑÑеннÑ.",Default:"Ðа замовÑÑваннÑм","Microphone {number}":"ÐÑкÑоÑон {number}","Mic Check":"ÐеÑевÑÑка мÑкÑоÑона","Recording Sync":"СинÑ
ÑонÑзаÑÑÑ Ð·Ð°Ð¿Ð¸ÑÑ","Left: earlier. Right: later.":"ÐÑвоÑÑÑ: ÑанÑÑе. ÐÑавоÑÑÑ: пÑзнÑÑе.","Voice timing":"СинÑ
ÑонÑзаÑÑÑ Ð³Ð¾Ð»Ð¾ÑÑ",Earlier:"РанÑÑе",Later:"ÐÑзнÑÑе","Input Level":"Ð ÑÐ²ÐµÐ½Ñ Ð²Ñ
одÑ","Stay Silent":"ÐбеÑÑгайÑе ÑиÑÑ","Measuring background noise for 2 seconds.":"ÐимÑÑÑÐ²Ð°Ð½Ð½Ñ Ñонового ÑÑÐ¼Ñ Ð¿ÑоÑÑгом 2 ÑекÑнд.","Speak Normally":"ÐовоÑÑÑÑ Ð·Ð²Ð¸Ñайним голоÑом","Record your voice at normal volume for 3-5 seconds.":"ÐовоÑÑÑÑ Ð·Ñ Ð·Ð²Ð¸ÑÐ°Ð¹Ð½Ð¾Ñ Ð³ÑÑнÑÑÑÑ 3â5 ÑекÑнд.","Stay silent for 2 seconds. Measuring background noise.":"ÐбеÑÑгайÑе ÑиÑÑ 2 ÑекÑнди. ÐимÑÑÑÑмо Ñоновий ÑÑм.","Speak at your normal volume for 3-5 seconds.":"ÐовоÑÑÑÑ Ð·Ñ Ð·Ð²Ð¸ÑÐ°Ð¹Ð½Ð¾Ñ Ð³ÑÑнÑÑÑÑ 3â5 ÑекÑнд.","This mic check is applied to future dubbing recordings.":"Це налаÑÑÑÐ²Ð°Ð½Ð½Ñ Ð¼ÑкÑоÑона заÑÑоÑовÑÑÑÑÑÑ Ð´Ð¾ майбÑÑнÑÑ
запиÑÑв озвÑÑеннÑ.","Compare the samples, then apply this cleanup to future recordings.":"ÐоÑÑвнÑйÑе зÑазки й заÑÑоÑÑйÑе оÑиÑÐµÐ½Ð½Ñ Ð´Ð¾ майбÑÑнÑÑ
запиÑÑв.","Your saved mic calibration is applied to future dubbing recordings on this browser.":"ÐбеÑежене калÑбÑÑÐ²Ð°Ð½Ð½Ñ Ð·Ð°ÑÑоÑовÑÑÑÑÑÑ Ð´Ð¾ майбÑÑнÑÑ
запиÑÑв озвÑÑÐµÐ½Ð½Ñ Ð² ÑÑÐ¾Ð¼Ñ Ð±ÑаÑзеÑÑ.","Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.":"ÐбеÑÑгайÑе ÑиÑÑ 2 ÑекÑнди, поÑÑм говоÑÑÑÑ Ð·Ð²Ð¸Ñайним голоÑом 3â5 ÑекÑнд. РезÑлÑÑÐ°Ñ Ð¼Ð¾Ð¶Ð½Ð° заÑÑоÑÑваÑи Ð´Ð»Ñ ÑÑмозаглÑÑÐµÐ½Ð½Ñ Ð² гÑÑ.","Use Mic Check to connect":"СкоÑиÑÑайÑеÑÑ Ð¿ÐµÑевÑÑÐºÐ¾Ñ Ð¼ÑкÑоÑона Ð´Ð»Ñ Ð¿ÑдклÑÑеннÑ","Requesting microphone":"ÐапиÑÑÑмо доÑÑÑп до мÑкÑоÑона","Microphone Ready":"ÐÑкÑоÑон гоÑовий","Permission denied":"ÐоÑÑÑп забоÑонено","Microphone Unavailable":"ÐÑкÑоÑон недоÑÑÑпний","Microphone may be occupied":"ÐÑкÑоÑон може викоÑиÑÑовÑваÑи ÑнÑа пÑогÑама","Selected device unavailable":"ÐибÑаний пÑиÑÑÑÑй недоÑÑÑпний","Device disconnected":"ÐÑиÑÑÑÑй вÑдклÑÑено","Calibration saved · mic reconnects when needed":"ÐалÑбÑÑÐ²Ð°Ð½Ð½Ñ Ð·Ð±ÐµÑежено · мÑкÑоÑон пÑдклÑÑаÑÑÑÑÑ Ð·Ð° поÑÑеби","Microphone could not start":"Ðе вдалоÑÑ Ð·Ð°Ð¿ÑÑÑиÑи мÑкÑоÑон","Mic calibration applied":"ÐалÑбÑÑÐ²Ð°Ð½Ð½Ñ Ð¼ÑкÑоÑона заÑÑоÑовано","Mic calibration not applied":"ÐалÑбÑÑÐ²Ð°Ð½Ð½Ñ Ð¼ÑкÑоÑона не заÑÑоÑовано","Noise cleanup is active for future recordings.":"ШÑмозаглÑÑÐµÐ½Ð½Ñ Ð°ÐºÑивне Ð´Ð»Ñ Ð¼Ð°Ð¹Ð±ÑÑнÑÑ
запиÑÑв.","Run Mic Check and apply it to improve noise cleanup.":"ÐапÑÑÑÑÑÑ Ð¿ÐµÑевÑÑÐºÑ Ð¼ÑкÑоÑона й заÑÑоÑÑйÑе ÑезÑлÑÑÐ°Ñ Ð´Ð»Ñ ÐºÑаÑого ÑÑмозаглÑÑеннÑ.",Applied:"ÐаÑÑоÑовано","Ready to apply":"ÐоÑово до заÑÑоÑÑваннÑ","Noise:":"ШÑм:","Volume:":"ÐÑÑнÑÑÑÑ:","Peaks:":"ÐÑки:",Raw:"ÐÑигÑнал",Cleaned:"ÐÑиÑений",Retake:"ÐовÑоÑиÑи","Apply Mic Check":"ÐаÑÑоÑÑваÑи пеÑевÑÑкÑ","Mic is too loud":"ÐÑкÑоÑон звÑÑиÑÑ Ð½Ð°Ð´Ñо голоÑно","Backgroun
2d noise is high":"ÐиÑокий Ñоновий ÑÑм","Voice is quiet":"ÐÐ¾Ð»Ð¾Ñ Ð·Ð²ÑÑиÑÑ ÑиÑ
о","Voice is loud":"ÐÐ¾Ð»Ð¾Ñ Ð·Ð²ÑÑиÑÑ Ð³Ð¾Ð»Ð¾Ñно","Mic sounds ready":"ÐÑкÑоÑон налаÑÑовано","Start Mic Check":"ÐоÑаÑи пеÑевÑÑкÑ","Connecting Mic...":"ÐÑдклÑÑÐµÐ½Ð½Ñ Ð¼ÑкÑоÑона...","Stop Recording":"ÐÑпиниÑи запиÑ",Record:"ÐапиÑаÑи","Retry Mic":"ÐовÑоÑиÑи пÑдклÑÑеннÑ",Replay:"ÐовÑоÑ","My Take":"ÐÑй запиÑ",Retry:"ÐапиÑаÑи зновÑ",Next:"ÐалÑ",Finish:"ÐавеÑÑиÑи",Skip:"ÐÑопÑÑÑиÑи","GET READY":"ÐÐ ÐÐÐТУÐТÐСЯ",RECORDING:"ÐÐÐÐС","CLIP {current} / {total}":"ÐÐÐÐ {current} / {total}","Jump ahead to another clip":"ÐеÑейÑи до ÑнÑого клÑпÑ","Replay original clip":"ÐовÑоÑиÑи оÑигÑналÑний клÑп","Finish pack":"ÐавеÑÑиÑи мод","Go to next clip":"ÐеÑейÑи до наÑÑÑпного клÑпÑ",Start:"ÐоÑаÑи","Starting...":"ÐапÑÑк...","Could not start this mod":"Ðе вдалоÑÑ Ð·Ð°Ð¿ÑÑÑиÑи Ñей мод","Copy start error details":"ÐопÑÑваÑи вÑдомоÑÑÑ Ð¿Ñо помилкÑ","Creator:":"ÐвÑоÑ:","Characters:":"ÐеÑÑонажÑ:","No Dub Mods Yet":"ÐодÑв озвÑÑÐµÐ½Ð½Ñ Ñе немаÑ","Subscribe to a pack from the Workshop, then start your game.":"ÐÑдпиÑÑÑÑÑÑ Ð½Ð° мод Ñ ÐайÑÑеÑнÑ, а поÑÑм поÑнÑÑÑ Ð³ÑÑ.","Browse Workshop":"ÐÑдкÑиÑи ÐайÑÑеÑнÑ","My Mods":"ÐÐ¾Ñ Ð¼Ð¾Ð´Ð¸","Refresh Mods":"ÐновиÑи моди","Refreshing Mods...":"ÐÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð´Ñв...","Refreshing...":"ÐновленнÑ...","Mods are up to date.":"Ðоди оновлено.","{count} mods":"ÐодÑв: {count}","{count} / syncing Workshop":"{count} / ÑинÑ
ÑонÑзаÑÑÑ Ð· ÐайÑÑеÑнеÑ","{count} / {name} judges in use":"{count} / викоÑиÑÑовÑÑÑÑÑÑ Ð¶ÑÑÑ Â«{name}»","{count} / default judges in use":"{count} / викоÑиÑÑовÑÑÑÑÑÑ ÑÑандаÑÑне жÑÑÑ","Local mod import options":"ÐаÑамеÑÑи ÑмпоÑÑÑ Ð»Ð¾ÐºÐ°Ð»ÑниÑ
модÑв","Import local file":"ÐмпоÑÑ Ð»Ð¾ÐºÐ°Ð»Ñного ÑайлÑ","Dub Mod ZIP":"ZIP мода озвÑÑеннÑ","Dub Mod Folder":"Ðапка мода озвÑÑеннÑ","Judge Mod ZIP":"ZIP мода жÑÑÑ","Search by mod, creator, character, or type":"ÐоÑÑк за модом, авÑоÑом, пеÑÑонажем або Ñипом","Search mods":"ÐоÑÑк модÑв","Clear search":"ÐÑиÑÑиÑи поÑÑк","Syncing Workshop subscriptions":"СинÑ
ÑонÑзаÑÑÑ Ð¿ÑдпиÑок ÐайÑÑеÑнÑ","Checking which installed mods belong to your account.":"ÐеÑевÑÑÑÑмо, ÑÐºÑ Ð²ÑÑÐ°Ð½Ð¾Ð²Ð»ÐµÐ½Ñ Ð¼Ð¾Ð´Ð¸ повâÑÐ·Ð°Ð½Ñ Ð· ваÑим облÑковим запиÑом.","No mods yet":"ÐодÑв Ñе немаÑ","Subscribe to a pack from the Workshop, then start a game in":"ÐÑдпиÑÑÑÑÑÑ Ð½Ð° мод Ñ ÐайÑÑеÑнÑ, а поÑÑм поÑнÑÑÑ Ð³ÑÑ Ð² ÑежимÑ",Solo:"ÐдиноÑна гÑа",or:"або"," game.":".",'No mods match "{query}"':"ÐÐµÐ¼Ð°Ñ Ð¼Ð¾Ð´Ñв за запиÑом «{query}»","Clear Search":"ÐÑиÑÑиÑи поÑÑк","View installation progress in Mods":"ÐеÑеглÑнÑÑи Ñ
Ñд ÑÑÑÐ°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð² ÑоздÑÐ»Ñ Â«Ðоди»","View in Mods":"ÐÑдкÑиÑи в модаÑ
","Show clip scores":"ÐоказаÑи оÑÑнки клÑпÑв","Reconnect microphone and record":"ÐеÑепÑдклÑÑиÑи мÑкÑоÑон Ñ Ð·Ð°Ð¿Ð¸ÑаÑи","Play your recorded take with background audio":"ÐÑдÑвоÑиÑи ÑвÑй Ð·Ð°Ð¿Ð¸Ñ Ñз Ñоновим звÑком","Play your recorded take":"ÐÑдÑвоÑиÑи ÑвÑй запиÑ","Background volume":"ÐÑÑнÑÑÑÑ ÑонÑ",Background:"Фон",Cancel:"СкаÑÑваÑи","Report Player":"ÐоÑкаÑжиÑиÑÑ Ð½Ð° гÑавÑÑ","Mod feedback":"ÐÑдгÑк пÑо мод","Report this mod":"ÐоÑкаÑжиÑиÑÑ Ð½Ð° мод","Report reason":"ÐÑиÑина ÑкаÑги","Incorrect role information":"ÐепÑавилÑна ÑнÑоÑмаÑÑÑ Ð¿Ñо ÑолÑ","Incorrect language":"Ð
2епÑавилÑна мова","Broken or misleading mod":"ÐеÑпÑавний або оманливий мод","Inappropriate content":"ÐепÑийнÑÑний вмÑÑÑ","Copyright or rights issue":"ÐоÑÑÑÐµÐ½Ð½Ñ Ð°Ð²ÑоÑÑÑкиÑ
Ñи ÑнÑиÑ
пÑав","Spam or abuse":"Спам або зловживаннÑ","Describe the problem":"ÐпиÑÑÑÑ Ð¿ÑоблемÑ","Send Report":"ÐадÑÑлаÑи ÑкаÑгÑ","Sending...":"ÐадÑиланнÑ...","Report received.":"СкаÑÐ³Ñ Ð¾ÑÑимано.","Report could not be submitted.":"Ðе вдалоÑÑ Ð½Ð°Ð´ÑÑлаÑи ÑкаÑгÑ.","Close report dialog":"ÐакÑиÑи вÑкно ÑкаÑги","Reported Player":"ÐÑавеÑÑ","Report Type":"Тип поÑÑÑеннÑ","Choose a violation type":"ÐибеÑÑÑÑ Ñип поÑÑÑеннÑ",Description:"ÐпиÑ","Describe what happened so moderators can review the report.":"ÐпиÑÑÑÑ, Ñо ÑÑалоÑÑ, Ñоб модеÑаÑоÑи могли ÑозглÑнÑÑи ÑкаÑгÑ.","At least 10 characters.":"ЩонайменÑе 10 ÑимволÑв.","Submit Report":"ÐадÑÑлаÑи ÑкаÑгÑ","Harassment / Abuse":"ÐÐ¾Ð¼Ð°Ð³Ð°Ð½Ð½Ñ / обÑази","Hate Speech":"Ðова воÑожнеÑÑ",Spam:"Спам","Inappropriate Content":"ÐепÑийнÑÑний вмÑÑÑ",Other:"ÐнÑе","Clip Scores":"ÐÑÑнки клÑпÑв","{completed} / {total} clips completed":"ÐавеÑÑено клÑпÑв: {completed} / {total}",Detail:"ÐокладнÑÑе","Back to final score":"ÐовеÑнÑÑиÑÑ Ð´Ð¾ пÑдÑÑмкового ÑаÑ
ÑнкÑ","No playable dub video is available.":"ÐÑдео озвÑÑÐµÐ½Ð½Ñ Ð½ÐµÐ´Ð¾ÑÑÑпне Ð´Ð»Ñ Ð²ÑдÑвоÑеннÑ.","Play my dubbed work":"ÐÑдÑвоÑиÑи Ð¼Ð¾Ñ Ð¾Ð·Ð²ÑÑеннÑ","PUBLIC MULTIPLAYER":"ÐУÐÐÐЧÐÐ ÐÐ Ð","Public Lobby":"ÐÑблÑÑне лобÑ","Refresh public rooms":"ÐновиÑи пÑблÑÑÐ½Ñ ÐºÑмнаÑи","Multiplayer Lobby":"ÐеÑежеве лобÑ","Refresh rooms":"ÐновиÑи кÑмнаÑи","All Languages":"УÑÑ Ð¼Ð¾Ð²Ð¸",Status:"СÑаÑÑÑ","All Rooms":"УÑÑ ÐºÑмнаÑи","Waiting Only":"ÐиÑе в оÑÑкÑваннÑ","In Progress":"ÐÑа ÑÑиваÑ",Results:"РезÑлÑÑаÑи",Waiting:"ÐÑÑкÑваннÑ","No public rooms match these filters.":"ÐÐµÐ¼Ð°Ñ Ð¿ÑблÑÑниÑ
кÑÐ¼Ð½Ð°Ñ Ñз Ñакими ÑÑлÑÑÑами.","Loading public rooms...":"ÐаванÑÐ°Ð¶ÐµÐ½Ð½Ñ Ð¿ÑблÑÑниÑ
кÑмнаÑ...","No rooms match these filters.":"ÐÐµÐ¼Ð°Ñ ÐºÑÐ¼Ð½Ð°Ñ Ñз Ñакими ÑÑлÑÑÑами.","Loading rooms...":"ÐаванÑÐ°Ð¶ÐµÐ½Ð½Ñ ÐºÑмнаÑ...","Room visibility":"ÐидимÑÑÑÑ ÐºÑмнаÑи",Private:"ÐÑиваÑна","Private Room":"ÐÑиваÑна кÑмнаÑа",Public:"ÐÑблÑÑна","Room Language":"Ðова кÑмнаÑи","Sign in to create a public room":"УвÑйдÑÑÑ, Ñоб ÑÑвоÑиÑи пÑблÑÑÐ½Ñ ÐºÑмнаÑÑ","Waiting for host to choose a mod":"ÐÑÑкÑÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð±Ð¾ÑÑ Ð¼Ð¾Ð´Ð° Ñ
оÑÑом",Join:"ÐÑиÑднаÑиÑÑ","View Profile":"ÐеÑеглÑнÑÑи пÑоÑÑлÑ","Host must start: {count}s":"ХоÑÑ Ð¼Ð°Ñ Ð¿Ð¾ÑаÑи: {count} Ñ","Room Chat":"Ð§Ð°Ñ ÐºÑмнаÑи","No messages yet.":"ÐовÑÐ´Ð¾Ð¼Ð»ÐµÐ½Ñ Ñе немаÑ.","Message the room":"ÐапиÑаÑи в кÑмнаÑÑ","Reconnecting chat...":"ÐовÑоÑне пÑдклÑÑÐµÐ½Ð½Ñ ÑаÑÑ...","Send message":"ÐадÑÑлаÑи повÑдомленнÑ","Open room chat":"ÐÑдкÑиÑи ÑÐ°Ñ ÐºÑмнаÑи","Close room chat":"ÐакÑиÑи ÑÐ°Ñ ÐºÑмнаÑи","Players online":"ÐÑавÑÑ Ð¾Ð½Ð»Ð°Ð¹Ð½","{count} unread messages":"ÐепÑоÑиÑаниÑ
повÑдомленÑ: {count}","{name} disconnected.":"ÐâÑÐ´Ð½Ð°Ð½Ð½Ñ Ð· {name} вÑÑаÑено.","{name} reconnected.":"{name} Ð·Ð½Ð¾Ð²Ñ Ð² меÑежÑ.","{name} left the room.":"{name} залиÑÐ°Ñ ÐºÑмнаÑÑ.","This public room has been waiting for 50 minutes.":"Ð¦Ñ Ð¿ÑблÑÑна кÑмнаÑа оÑÑкÑÑ Ð²Ð¶Ðµ 50 Ñ
вилин.","Player Name":"ÐмâÑ Ð³ÑавÑÑ","Create Room":"СÑвоÑиÑи кÑмнаÑÑ","Creating...":"СÑвоÑеннÑ...","Room Code":"Ðод кÑмнаÑи","Join Room":"УвÑ
2йÑи до кÑмнаÑи","Joining...":"ÐÑдклÑÑеннÑ...","Leave Room":"ÐокинÑÑи кÑмнаÑÑ","ROOM CODE":"ÐÐÐ ÐÐÐÐÐТÐ","Copy Code":"ÐопÑÑваÑи код","Copy Invite Link":"ÐопÑÑваÑи поÑиланнÑ",Copied:"СкопÑйовано",Host:"ХоÑÑ",Ready:"ÐоÑовий",Rendering:"СÑвоÑÑÑÑÑÑÑ","Not rendered":"Ðе ÑÑвоÑено","Waiting â click to switch rendering":"ÐÑÑкÑÐ²Ð°Ð½Ð½Ñ â наÑиÑнÑÑÑ, Ñоб пеÑемкнÑÑи обÑобкÑ","Not Ready":"Ðе гоÑовий","Kick Player":"ÐиклÑÑиÑи гÑавÑÑ","Host actions for {name}":"ÐÑÑ Ñ
оÑÑа Ð´Ð»Ñ {name}","Empty player slot":"ÐÑлÑне мÑÑÑе гÑавÑÑ","Ready Up":"ÐоÑовий","Cancel Ready":"СкаÑÑваÑи гоÑовнÑÑÑÑ","Installing room mod":"УÑÑÐ°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð´Ð° кÑмнаÑи","Installing room mod {percent}%":"УÑÑÐ°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¼Ð¾Ð´Ð° кÑмнаÑи: {percent}%","Start Game":"ÐоÑаÑи гÑÑ","Starting in {count}":"ÐоÑаÑок ÑеÑез {count}","How multiplayer mods sync":"Як ÑинÑ
ÑонÑзÑÑÑÑÑÑ Ð¼ÐµÑÐµÐ¶ÐµÐ²Ñ Ð¼Ð¾Ð´Ð¸","Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.":"ÐÑдпиÑки ÐайÑÑеÑÐ½Ñ Ð²ÑÑановлÑÑÑÑÑÑ Ð°Ð²ÑомаÑиÑно Ð´Ð»Ñ Ð³ÑавÑÑв, ÑÐºÑ Ð²Ð²ÑйÑли. ÐокалÑÐ½Ñ Ð¼Ð¾Ð´Ð¸ Ð¼Ð°Ñ Ð·Ð°Ð²Ð°Ð½ÑажиÑи й ÑмпоÑÑÑваÑи кожен ÑÑаÑник кÑмнаÑи.","Change Mod":"ÐмÑниÑи мод","Choose a Mod":"ÐибеÑÑÑÑ Ð¼Ð¾Ð´","Import a mod first":"СпоÑаÑÐºÑ ÑмпоÑÑÑйÑе мод","Multiplayer mode":"Режим меÑÐµÐ¶ÐµÐ²Ð¾Ñ Ð³Ñи","Co-op":"СпÑлÑна гÑа","Rotate turns or let players claim open clips.":"ÐеÑедавайÑе Ñ
Ñд по ÑеÑÐ·Ñ Ð°Ð±Ð¾ дозволÑÑе гÑавÑÑм вибиÑаÑи вÑлÑÐ½Ñ ÐºÐ»Ñпи.","Everyone records the same clips and compares scores.":"УÑÑ Ð·Ð°Ð¿Ð¸ÑÑÑÑÑ Ð¾Ð´Ð½Ð°ÐºÐ¾Ð²Ñ ÐºÐ»Ñпи й поÑÑвнÑÑÑÑ ÑезÑлÑÑаÑи.","Co-op Turn Mode":"ÐоÑÑдок Ñ
одÑв Ñ ÑпÑлÑнÑй гÑÑ","Co-op Assignment":"РозподÑл Ñолей Ñ Ñ
одÑв","Rotate turns, claim clips, or claim roles.":"ÐÑайÑе по ÑеÑÐ·Ñ Ð°Ð±Ð¾ обиÑайÑе клÑпи й ÑолÑ.","Round Robin":"Ðо ÑеÑзÑ","Claim Mode":"ÐибÑÑ ÐºÐ»ÑпÑ","Role Claim":"ÐибÑÑ ÑолÑ","Co-op · Role Claim":"СпÑлÑна гÑа · ÐибÑÑ Ñолей","Choose Roles":"ÐбÑаÑи ÑолÑ","Back to room":"ÐовеÑнÑÑиÑÑ Ð´Ð¾ кÑмнаÑи","All roles are assigned.":"УÑÑ ÑÐ¾Ð»Ñ ÑозподÑленÑ.","Start Dubbing":"ÐоÑаÑи озвÑÑеннÑ","Waiting for the host to start dubbing.":"ÐÑÑкÑÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÑаÑÐºÑ Ð¾Ð·Ð²ÑÑÐµÐ½Ð½Ñ Ð²Ñд ведÑÑого.","Syncing role selection":"СинÑ
ÑонÑзаÑÑÑ Ñолей","This mod has no roles to claim":"У ÑÑÐ¾Ð¼Ñ Ð¼Ð¾Ð´Ñ Ð½ÐµÐ¼Ð°Ñ Ñолей Ð´Ð»Ñ Ð²Ð¸Ð±Ð¾ÑÑ","Claim every role before starting":"ÐеÑед поÑаÑком ÑозподÑлÑÑÑ ÑÑÑ ÑолÑ","Choose your roles":"ÐбеÑÑÑÑ ÑолÑ","Choose your roles.":"ÐбеÑÑÑÑ ÑолÑ.",Claim:"ÐбÑаÑи ÑолÑ",Release:"ÐвÑлÑниÑи ÑолÑ","Returned to lobby.":"ÐовеÑÐ½ÐµÐ½Ð½Ñ Ð´Ð¾ кÑмнаÑи оÑÑкÑваннÑ.","Could not start role selection.":"Ðе вдалоÑÑ ÑозпоÑаÑи вибÑÑ Ñолей.","Could not return to lobby.":"Ðе вдалоÑÑ Ð¿Ð¾Ð²ÐµÑнÑÑиÑÑ Ð´Ð¾ кÑмнаÑи оÑÑкÑваннÑ.","Could not update role.":"Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñи ÑолÑ.","Session complete":"РаÑнд завеÑÑено","Click a role to claim it. Click again to release it.":"ÐаÑиÑнÑÑÑ Ð½Ð° ÑолÑ, Ñоб обÑаÑи ÑÑ. ÐаÑиÑнÑÑÑ Ñе Ñаз, Ñоб звÑлÑниÑи.","Preview lines":"ÐеÑеглÑнÑÑи ÑеплÑки","Loading lines...":"ÐаванÑÐ°Ð¶ÐµÐ½Ð½Ñ ÑеплÑк...","LINE PREVIEW":"ÐÐÐ ÐÐÐЯРРÐÐÐÐÐ","{count} lines":"РеплÑк: {count}","Filter lines by role":"ФÑлÑÑÑ ÑеплÑк за ÑоллÑ",All:"УÑÑ",Unassigned:"Ðез ÑолÑ","No lines found for this role.":"ÐÐ»Ñ ÑÑÑÑ ÑÐ¾Ð»Ñ ÑеплÑк не знайдено.",claimed:"зайнÑÑо","Claim a role before the host starts. You can take more than one.":"ÐбеÑÑÑÑ ÑÐ¾Ð»Ñ Ð´Ð¾ поÑаÑÐºÑ Ð³Ñи. Ðожна взÑÑи к
2ÑлÑка Ñолей.","Randomize Player Order":"Ðипадковий поÑÑдок гÑавÑÑв","Shuffles the player order once when the game starts, then continues rotating fairly.":"ÐеÑемÑÑÑÑ Ð³ÑавÑÑв один Ñаз на поÑаÑкÑ, пÑÑÐ»Ñ Ñого Ñ
оди ÑпÑаведливо йдÑÑÑ Ð¿Ð¾ колÑ.","PVP Format":"ФоÑÐ¼Ð°Ñ PVP","Turn Battle":"ÐиÑва за Ñ
одами","Parallel Race":"ÐаÑалелÑÐ½Ñ Ð¿ÐµÑегони","Players record separately. Faster players get a higher score.":"ÐÑавÑÑ Ð·Ð°Ð¿Ð¸ÑÑÑÑÑÑÑ Ð¾ÐºÑемо. ШвидÑÑ Ð¾ÑÑимÑÑÑÑ Ð±ÑлÑÑе балÑв.","Players take turns recording the same clip before moving to the next one.":"ÐÑавÑÑ Ð¿Ð¾ ÑеÑÐ·Ñ Ð·Ð°Ð¿Ð¸ÑÑÑÑÑ Ð¾Ð´Ð¸Ð½ клÑп, а поÑÑм пеÑеÑ
одÑÑÑ Ð´Ð¾ наÑÑÑпного.","Allow Re-records":"ÐозволиÑи пеÑезапиÑ","Player Limit":"ÐÑмÑÑ Ð³ÑавÑÑв",Confirm:"ÐÑдÑвеÑдиÑи","On: players can clear their current multiplayer take and record it again before submitting.":"ÐÑавÑÑ Ð¼Ð¾Ð¶ÑÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñи поÑоÑний меÑежевий Ð·Ð°Ð¿Ð¸Ñ Ñ Ð·Ð°Ð¿Ð¸ÑаÑи його Ð·Ð½Ð¾Ð²Ñ Ð¿ÐµÑед надÑиланнÑм.","DUB MOD":"ÐÐÐ ÐÐÐУЧÐÐÐЯ","{count} players in room":"ÐÑавÑÑв Ñ ÐºÑмнаÑÑ: {count}","Score Judging":"ÐÑдÑаÑ
Ñнок оÑÑнки","Judge feedback":"ÐÑдгÑки ÑÑддÑв","Judge score stage":"СÑена ÑÑддÑвÑÑкиÑ
оÑÑнок","Preparing score":"ÐÑдгоÑовка оÑÑнки","Your {label} is":"Ðоказник «{label}»:","Final score {score}":"ÐÑдÑÑмковий ÑаÑ
Ñнок: {score}",pending:"оÑÑкÑваннÑ",Continue:"ÐÑодовжиÑи",Duration:"ТÑивалÑÑÑÑ",Timing:"ТаймÑнг",Energy:"ÐнеÑгÑÑ",Pitch:"ÐиÑоÑа ÑонÑ","Perfect Fit":"ÐдеалÑно","Good Fit":"ÐобÑе",Uneven:"ÐеÑÑвно","Too Short/Long":"ÐадÑо коÑоÑко/довго","Locked In":"ТоÑно в ÑиÑм","On Time":"ÐÑаÑно",Close:"ÐакÑиÑи","Off Beat":"Ðовз ÑиÑм","Stage Ready":"ÐоÑово до ÑÑени",Expressive:"ÐиÑазно",Solid:"Упевнено",Flat:"Ðез емоÑÑй",Uncanny:"ÐеймовÑÑно","Very Close":"ÐÑже близÑко","Close Enough":"ÐоÑиÑÑ Ð±Ð»Ð¸Ð·Ñко",Different:"ÐÑдÑÑзнÑÑÑÑÑÑ","Perfect Dub":"ÐдеалÑне озвÑÑеннÑ","Viral Performance":"ÐÑÑÑÑний виÑÑÑп","Scene Stealer":"ÐÑÑка ÑÑени","Great Take":"ЧÑдовий дÑблÑ","Solid Dub":"ÐобÑе озвÑÑеннÑ","Getting There":"Уже непогано","Keep Practicing":"ÐÑодовжÑйÑе ÑÑенÑваÑиÑÑ","Download exports":"ÐаванÑажиÑи ÑезÑлÑÑаÑи","Download My Work":"ÐаванÑажиÑи Ð¼Ð¾Ñ ÑобоÑÑ","Your browser needs one more tap to play this mod video.":"ÐаÑиÑнÑÑÑ Ñе Ñаз, Ñоб бÑаÑÐ·ÐµÑ Ð²ÑдÑвоÑив вÑдео модÑ.","Continue Export":"ÐÑодовжиÑи екÑпоÑÑ","Video subtitle option":"ÐалаÑÑÑÐ²Ð°Ð½Ð½Ñ ÑÑбÑиÑÑÑв вÑдео",Clean:"Ðез ÑÑбÑиÑÑÑв",Subtitles:"СÑбÑиÑÑи","Download my vertical Shorts video":"ÐаванÑажиÑи веÑÑикалÑне вÑдео Shorts","Save Shorts":"ÐбеÑегÑи Shorts","My Shorts":"ÐÐ¾Ñ Shorts",Video:"ÐÑдео","Download my landscape video":"ÐаванÑажиÑи гоÑизонÑалÑне вÑдео","Save Landscape":"ÐбеÑегÑи вÑдео","My Landscape":"ÐÐ¾Ñ Ð³Ð¾ÑизонÑалÑне","Download my audio track":"ÐаванÑажиÑи аÑдÑодоÑÑжкÑ","Save Audio":"ÐбеÑегÑи аÑдÑо","My Audio":"ÐÐ¾Ñ Ð°ÑдÑо",Track:"ÐоÑÑжка","Keep this page open while preparing. Long videos can take several minutes. When ready, tap Save to use the iPhone save menu.":"Ðе закÑивайÑе ÑÑоÑÑÐ½ÐºÑ Ð¿Ñд ÑÐ°Ñ Ð¿ÑдгоÑовки. ÐÐ¾Ð²Ð³Ñ Ð²Ñдео можÑÑÑ Ð¾Ð±ÑоблÑÑиÑÑ ÐºÑлÑка Ñ
вилин. ÐÑÑÐ»Ñ Ð·Ð°Ð²ÐµÑÑÐµÐ½Ð½Ñ Ð½Ð°ÑиÑнÑÑÑ Â«ÐбеÑегÑи», Ñоб вÑдкÑиÑи Ð¼ÐµÐ½Ñ iPhone.","Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.":"ÐкÑпоÑÑ Ð²Ð¸ÐºÐ¾Ð½ÑÑÑÑÑÑ Ñ WebM. Ðл
2Ñ Ð²ÑдÑвоÑÐµÐ½Ð½Ñ Ñекомендовано Chrome; ÑкÑо поÑÑÑбен MP4, пеÑеÑвоÑÑÑÑ Ñайл ÑамоÑÑÑйно.","Share exports":"ÐодÑлиÑиÑÑ ÑезÑлÑÑаÑами",Share:"ÐодÑлиÑиÑÑ","Copy share caption":"ÐопÑÑваÑи ÑекÑÑ Ð¿ÑблÑкаÑÑÑ","Copy caption":"ÐопÑÑваÑи ÑекÑÑ","Rotate your phone":"ÐовеÑнÑÑÑ ÑелеÑон","Landscape gives the video and recording controls more room.":"РалÑбомнÑй оÑÑÑнÑаÑÑÑ Ð±ÑлÑÑе мÑÑÑÑ Ð´Ð»Ñ Ð²Ñдео й кеÑÑÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñом.","Stay in portrait":"ÐалиÑиÑиÑÑ Ð² поÑÑÑеÑÐ½Ð¾Ð¼Ñ ÑежимÑ","Previous run ended unexpectedly":"ÐопеÑÐµÐ´Ð½Ñ Ð³Ñа неÑподÑвано завеÑÑилаÑÑ","Voxalike reloaded while a game was active. Copy this report if the interruption happens again.":"Voxalike пеÑезаванÑаживÑÑ Ð¿Ñд ÑÐ°Ñ Ð³Ñи. СкопÑÑйÑе Ñей звÑÑ, ÑкÑо збÑй повÑоÑиÑÑÑÑ.","Copy crash report":"СкопÑÑваÑи звÑÑ Ð¿Ñо збÑй","Dismiss crash report":"ÐакÑиÑи звÑÑ Ð¿Ñо збÑй","Page interrupted":"РобоÑÑ ÑÑоÑÑнки пеÑеÑвано","Your room and locally saved recordings can be restored after reloading.":"ÐÑÑÐ»Ñ Ð¿ÐµÑезаванÑÐ°Ð¶ÐµÐ½Ð½Ñ Ð¼Ð¾Ð¶Ð½Ð° вÑдновиÑи кÑмнаÑÑ Ð¹ локалÑÐ½Ñ Ð·Ð°Ð¿Ð¸Ñи.","Reload Voxalike":"ÐеÑезаванÑажиÑи Voxalike","ROOM CLOSING SOON":"ÐÐÐÐÐТРÐÐÐÐÐÐÐ ÐÐ ÐÐÐÐ ÐÐТЬСЯ","This room has been waiting for 10 minutes.":"Ð¦Ñ ÐºÑмнаÑа оÑÑкÑÑ Ð²Ð¶Ðµ 10 Ñ
вилин.","Start the game within the next 10 minutes or the room will close automatically.":"ÐоÑнÑÑÑ Ð³ÑÑ Ð¿ÑоÑÑгом наÑÑÑпниÑ
10 Ñ
вилин, ÑнакÑе кÑмнаÑа закÑиÑÑÑÑÑ Ð°Ð²ÑомаÑиÑно.","Room closed because no game started within 20 minutes.":"ÐÑмнаÑÑ Ð·Ð°ÐºÑиÑо, оÑкÑлÑки гÑÑ Ð½Ðµ бÑло ÑозпоÑаÑо пÑоÑÑгом 20 Ñ
вилин.","Room closed because the host did not reconnect within 5 minutes.":"ÐÑмнаÑÑ Ð·Ð°ÐºÑиÑо, оÑкÑлÑки ведÑÑий не пÑдклÑÑивÑÑ Ð·Ð½Ð¾Ð²Ñ Ð¿ÑоÑÑгом 5 Ñ
вилин.","Wait for disconnected players to reconnect":"ÐаÑекайÑе, доки вÑдâÑÐ´Ð½Ð°Ð½Ñ Ð³ÑавÑÑ Ð¿ÑдклÑÑаÑÑÑÑ Ð·Ð½Ð¾Ð²Ñ",Reconnecting:"ÐовÑоÑне пÑдклÑÑеннÑ","Game server updated":"ÐгÑовий ÑеÑÐ²ÐµÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð¾","Connection interrupted":"ÐâÑÐ´Ð½Ð°Ð½Ð½Ñ Ð¿ÐµÑеÑвано","Syncing the latest game server and restoring your room.":"СинÑ
ÑонÑзÑÑмо оÑÑÐ°Ð½Ð½Ñ Ð²ÐµÑÑÑÑ ÑгÑового ÑеÑвеÑа Ñа вÑдновлÑÑмо кÑмнаÑÑ.","Your connection dropped. Trying to reconnect to your room.":"ÐâÑÐ´Ð½Ð°Ð½Ð½Ñ Ð²ÑÑаÑено. ÐамагаÑмоÑÑ Ð¿Ð¾Ð²ÑоÑно пÑдклÑÑиÑиÑÑ Ð´Ð¾ кÑмнаÑи."}},xw=m.createContext({locale:"en",setLocale:()=>{},t:(e,o)=>Rw(e,o)});function cj(e=pj()){for(const o of e){const r=o.trim().toLowerCase();if(r==="de"||r.startsWith("de-"))return"de";if(r==="uk"||r.startsWith("uk-"))return"uk";if(r==="ru"||r.startsWith("ru-"))return"ru";if(r==="nl"||r.startsWith("nl-"))return"nl";if(r==="fr"||r.startsWith("fr-"))return"fr";if(r==="it"||r.startsWith("it-"))return"it";if(r==="es"||r.startsWith("es-"))return"es";if(r==="pt"||r.startsWith("pt-"))return"pt"}return"en"}function lj(e,o,r){const i=e==="en"?o:sj[e][o]??o;return Rw(i,r)}function dj({children:e}){const[o,r]=m.useState(uj);m.useEffect(()=>{document.documentElement.lang=o,document.cookie=`${nj}=${o}; Path=/; Max-Age=31536000; SameSite=Lax`;try{localStorage.setItem(Sw,o)}catch{}},[o]);const i=m.useMemo(()=>({locale:o,setLocale:r,t:(c,l)=>lj(o,c,l)}),[o]);return t.jsx(xw.Provider,{value:i,children:e})}function Be(){return m.useContext(xw)}function uj(){try{const e=localStorage.getItem(Sw);if(e==="en"||e==="de"||e==="ru"||e==="uk"||e==="nl"||e==="fr"||e==="it"||e==="es"||e==="pt")return e}catch{}return cj()}function pj(){return typeof navigator>"u"?[]:navigator.languages?.length?navigator.languages:[navigator.language]}function Rw(e,o){return o?e.replace(/\{(\w+)\}/g,(r,i)=>String(o[i]??r)):e}const By=["Incorrect role information","Incorrect language","Broken or misleading mod","Inappropriate content","Copyright or rights issue","Spam or abuse"];function mj({packTitle:e,packId:o,packSlug:r}){const{t:i}=Be(),[c,l]=m.useState(!1),[p,f]=m.useState(By[0]),[b,y]=m.useState(""),[w,M]=m.useState(!1),[E,z]=m.useState(!1),[N,j]=m.useState("");m.useEffect(()=>{if(!c)return;const I=B=>{B.key==="Escape"&&!w&&l(!1)};return document.addEventListener("keydown",I),()=>document.removeEventListener("keydown",I)},[c,w]);async function D(I){if(I.preventDefault(),!(w||!b.trim())){M(!0),j("");try{await KM({id:o,slug:r,reason:p,details:b.trim()}),z(!0),l(!1)}catch(B){j(B instanceof Error?B.message:i("Report could not be submitted."))}finally{M(!1)}}}return t.jsxs("section",{className:"result-action-section result-mod-report","aria-label":i("Report this mod"),children:[E?t.jsx("span",{className:"result-mod-report-success",role:"status","aria-label":i("Report received."),title:i("Report received."),children:t.jsx(nt,{size:18})}):t.jsx("button",{className:"ghost-button result-mod-report-trigger",type:"button",onClick:()=>l(!0),"aria-label":i("Report this mod"),title:i("Report this mod"),children:t.jsx(LC,{size:17})}),c?th.createPortal(t.jsx("div",{className:"mod-report-backdrop",onMouseDown:I=>{I.target===I.currentTarget&&!w&&l(!1)},children:t.jsxs("section",{className:"mod-report-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"mod-report-title",children:[t.jsxs("header",{children:[t.jsxs("div",{children:[t.jsx("h2",{id:"mod-report-title",children:i("Report this mod")}),t.jsx("p",{children:e})]}),t.jsx("button",{className:"icon-button",type:"button",disabled:w,onClick:()=>l(!1),"aria-label":i("Close report dialog"),children:t.jsx(Tt,{size:19})})]}),t.jsxs("form",{onSubmit:I=>{D(I)},children:[t.jsxs("label",{children:[t.jsx("span",{children:i("Report reason")}),t.jsx("select",{value:p,onChange:I=>f(I.target.value),disabled:w,children:By.map(I=>t.jsx("option",{value:I,children:i(I)},I))})]}),t.jsxs("label",{children:[t.jsx("span",{children:i("Description")}),t.jsx("textarea",{value:b,onChange:I=>y(I.target.value.slice(0,500)),maxLength:500,rows:5,required:!0,disabled:w,placeholder:i("Describe the problem")})]}),N?t.jsx("p",{className:"mod-report-error",role:"alert",children:N}):null,t.jsxs("div",{className:"mod-report-actions",children:[t.jsx("button",{className:"ghost-button",type:"button",disabled:w,onClick:()=>l(!1),children:i("Cancel")}),t.jsx("button",{className:"action-button",type:"submit",disabled:w||!b.trim(),children:i(w?"Sending...":"Send Report")})]})]})]})}),document.body):null]})}const Pw=Date.parse("2026-09-09T06:59:59Z");function sh(e=Date.now()){return e<=Pw}function Wy(e){if(!e?.length)return[];const o=new Set,r=[];return e.forEach(i=>
2{const c=i.trim();!c||o.has(c)||(o.add(c),r.push(c))}),r}function hj(e){return e?.some(o=>/\bsound could not be decoded and will be skipped\.$/i.test(o.trim()))??!1}const ch={targetRmsDb:-18,maxPeakDb:-1,maxAutoGainDb:8,trimFrameMs:20,trimHopMs:10,trimPaddingMs:160,trimNoiseMultiplier:2.2,minimumVoiceMs:100,noiseGateFrameMs:24,noiseGateHopMs:12,noiseGateOpenMultiplier:1.8,noiseGateCloseMultiplier:1.25,noiseGateFloorGainDb:-14,noiseGateMinimumDb:-64,noiseGateAttackMs:10,noiseGateReleaseMs:220,compressorThresholdDb:-6,compressorRatio:1.6},gj={...ch,trimPaddingMs:140,trimNoiseMultiplier:2.5,minimumVoiceMs:120,noiseGateOpenMultiplier:2.1,noiseGateCloseMultiplier:1.4,noiseGateFloorGainDb:-18,noiseGateMinimumDb:-62,noiseGateAttackMs:12,noiseGateReleaseMs:180},Qe={mediaConstraints:{echoCancellation:!0,noiseSuppression:!1,autoGainControl:!1,channelCount:1},processing:ch,processingProfiles:{natural:ch,clean:gj},features:{analysisSampleRate:16e3,energyFrameMs:30,energyHopMs:20,pitchFrameMs:40,pitchHopMs:30,minPitchHz:70,maxPitchHz:420,pitchConfidenceThreshold:.34}};function fj(e){return{...Qe.mediaConstraints,echoCancellation:e!=="raw",noiseSuppression:e!=="raw",autoGainControl:!1}}function $y(e){return e==="clean"?Qe.processingProfiles.clean:Qe.processingProfiles.natural}function Ni(e){return e==="clean"}function so(e){if(e.length===0)return 0;let o=0;for(const r of e)o+=r*r;return Math.sqrt(o/e.length)}function bj(e,o){if(e.length===0||o<=0)return 0;const r=Gi(e,o,40,20,!0);return _d(r,.35)}function br(e){let o=0;for(const r of e){const i=Math.abs(r);i>o&&(o=i)}return o}function ea(e){return e<=0?Number.NEGATIVE_INFINITY:20*Math.log10(e)}function et(e){return 10**(e/20)}function zh(e){if(e.length===0)return new Float32Array;let o=0;for(const i of e)o+=i;o/=e.length;const r=new Float32Array(e.length);for(let i=0;i<e.length;i+=1)r[i]=e[i]-o;return r}function Oh(e){const o=new Float32Array(e.length);for(let r=0;r<e.numberOfChannels;r+=1){const i=e.getChannelData(r);for(let c=0;c<e.length;c+=1)o[c]+=i[c]/e.numberOfChannels}return{samples:o,sampleRate:e.sampleRate}}function Cw(e,o,r=0,i=Qe.processing){if(e.length===0)return{samples:new Float32Array,sampleRate:o,detectedVoice:!1,startSample:0,endSample:0};const c=Math.max(1,Math.round(i.trimFrameMs/1e3*o)),l=Math.max(1,Math.round(i.trimHopMs/1e3*o)),p=Math.round(i.trimPaddingMs/1e3*o),f=Gi(e,o,i.trimFrameMs,i.trimHopMs),b=Uh(e,o,r,i),y=Math.max(et(-54),b.noiseFloorRms*b.trimMultiplier,b.absoluteRms*.03);let w=-1,M=-1;for(let D=0;D<f.length;D+=1){const I=D*l,B=Math.min(e.length,I+c);f[D]>y&&(w<0&&(w=I),M=B)}if(w<0||M<0)return{samples:new Float32Array(e),sampleRate:o,detectedVoice:!1,startSample:0,endSample:e.length};const E=Math.max(0,w-p),z=Math.min(e.length,M+p),N=Math.round(i.minimumVoiceMs/1e3*o),j=M-w>=N;return{samples:e.slice(E,z),sampleRate:o,detectedVoice:j,startSample:E,endSample:z}}function Pc(e,o){const r=new Float32Array(e.length);for(let i=0;i<e.length;i+=1)r[i]=e[i]*o;return r}function Gy(e,o=et(Qe.processing.maxPeakDb)){const r=new Float32Array(e.length);for(let i=0;i<e.length;i+=1){const c=e[i];r[i]=Math.max(-o,Math.min(o,c))}return r}function yj(e,o,r=0,i=Qe.processing){const c=new Float32Array(e);if(c.length===0)return c;const l=Math.max(1,Math.round(i.noiseGateHopMs/1e3*o)),p=Gi(c,o,i.noiseGateFrameMs,i.noiseGateHopMs),f=Uh(c,o,r,i,p);if(!f.hasSpeechLikeRange)return c;const b=et(i.noiseGateMinimumDb),y=Math.max(f.noiseFloorRms*f.gateOpenMultiplier,f.loudRms*.11,b),w=Math.max(f.noiseFloorRms*f.gateCloseMultiplier,y*.45),M=et(f.gateFloorGainDb),E=Math.min(1,l/Math.max(1,Math.round(i.noiseGateAttackMs/1e3*o))),z=Math.min(1,l/Math.max(1,Math.round(f.gateReleaseMs/1e3*o))),N=[],j=B=>{let L=M;if(B>=y)L=1;else if(B>w){const _=(B-w)/Math.max(1e-6,y-w);L=M+(1-M)*Math.max(0,Math.min(1,_))}return L};let D=j(p[0]??0);for(const B of p){const L=j(B),_=L>D?E:z;D+=(L-D)*_,N.push(D)}const I=new Float32Array(c.length);for(let B=0;B<c.length;B+=1){const L=B/l,_=Math.min(N.length-1,Math.floor(L)),oe=Math.min(N.length-1,_+1),W=L-_,O=N[_]*(1-W)+N[oe]*W;I[B]=c[B]*O}return I}function qy(e,o,r=0,i=Qe.processing){const c=zh(e),l=jw(c,o,r,i),p=Cw(c,o,l,i),f=so(p.samples),b=br(p.samples);if(Mw(c,o,l,i)||!p.detectedVoice||f<=l*1.2||f===0){const j=Pc(p.samples,et(i.noiseGateFloorGainDb));return{samples:j,sampleRate:o,detectedVoice:!1,originalRms:f,processedRms:so(j),originalPeak:b,processedPeak:br(j),appliedGainDb:0,trimStartSample:p.startSample,trimEndSample:p.endSample}}const w=et(i.targetRmsDb),M=et(i.maxAutoGainDb),E=et(i.maxPeakDb),z=Math.max(1,Math.min(M,w/f)),N=Ew(p.samples,o,z,l,E,i);return{samples:N,sampleRate:o,detectedVoice:p.detectedVoice,originalRms:f,processedRms:so(N),originalPeak:b,processedPeak:br(N),appliedGainDb:ea(z),trimStartSample:p.startSample,trimEndSample:p.endSample}}function vj(e,o){if(!Number.isFinite(o)||o<=0)return{samples:new Float32Array(e.samples),sampleRate:e.sampleRate};const r=Math.max(1,Math.round(o*e.sampleRate)),i=new Float32Array(r);return i.set(e.samples.subarray(0,r)),{samples:i,sampleRate:e.sampleRate}}function Hy(e,o=0,r=Qe.processing){const i=zh(e.samples),c=jw(i,e.sampleRate,o,r),l=so(i),p=br(i),f=Cw(i,e.sampleRate,c,r);if(Mw(i,e.sampleRate,c,r)||!f.detectedVoice||l<=c*1.2||l===0||p===0)return{samples:Pc(i,et(r.noiseGateFloorGainDb)),sampleRate:e.sampleRate};const b=et(r.targetRmsDb),y=et(r.maxAutoGainDb),w=et(r.maxPeakDb),M=so(f.samples),E=Math.max(1,Math.min(y,b/Math.max(M,l)));return{samples:Ew(i,e.sampleRate,E,c,w,r),sampleRate:e.sampleRate}}function Jy(e){const o=e.reduce((c,l)=>c+l.length,0),r=new Float32Array(o);let i=0;for(const c of e)r.set(c,i),i+=c.length;return r}function Gi(e,o,r,i,c=!1){const l=Math.max(1,Math.round(r/1e3*o)),p=Math.max(1,Math.round(i/1e3*o)),f=[];for(let b=0;b<e.length;b+=p){const y=e.subarray(b,Math.min(e.length,b+l));f.push(so(c?zh(y):y))}return f}function Mw(e,o,r,i){const c=Gi(e,o,i.noiseGateFrameMs,i.noiseGateHopMs),l=Uh(e,o,r,i,c),p=so(e),f=br(e);return(p>0||f>
20)&&!l.hasSpeechLikeRange}function _y(e,o){const r=Math.max(e,et(-72));return o>=r*1.55||o-r>=et(-44)}function _d(e,o){if(e.length===0)return 0;const r=[...e].sort((c,l)=>c-l),i=Math.max(0,Math.min(r.length-1,Math.round((r.length-1)*o)));return r[i]}function Uh(e,o,r,i,c){const l=c??Gi(e,o,i.noiseGateFrameMs,i.noiseGateHopMs),p=_d(l,.2),f=_d(l,.88),b=et(i.noiseGateMinimumDb),y=_y(p,f)?p:Math.min(p,f),w=Tw(r,f),M=Math.max(w,y,b),E=so(e),z=Math.max(f,E,b),N=ea(z/Math.max(M,1e-6)),j=wj((N-4)/14,0,1);return{noiseFloorRms:M,loudRms:f,absoluteRms:E,signalToNoiseDb:N,trimMultiplier:tc(1.45,i.trimNoiseMultiplier,j),gateOpenMultiplier:tc(1.55,i.noiseGateOpenMultiplier,j),gateCloseMultiplier:tc(1.12,i.noiseGateCloseMultiplier,j),gateFloorGainDb:tc(-14,i.noiseGateFloorGainDb,j),gateReleaseMs:tc(150,i.noiseGateReleaseMs,j),hasSpeechLikeRange:_y(M,f)}}function jw(e,o,r,i){if(!Number.isFinite(r)||r<=0||e.length===0)return 0;const c=Gi(e,o,i.noiseGateFrameMs,i.noiseGateHopMs);return Tw(r,_d(c,.88))}function Tw(e,o){return!Number.isFinite(e)||e<=0||!Number.isFinite(o)||o<=0?0:Math.min(e,o/2.5)}function Ew(e,o,r,i,c,l){const p=Gy(Pc(e,r),1),f=yj(p,o,i>0?i*r:0,l),b=kj(f,et(l.compressorThresholdDb),l.compressorRatio);return Gy(b,c)}function kj(e,o,r){const i=new Float32Array(e.length);for(let c=0;c<e.length;c+=1){const l=e[c],p=Math.abs(l);if(p<=o||p===0){i[c]=l;continue}const f=o*(p/o)**(1/r);i[c]=Math.sign(l)*f}return i}function wj(e,o,r){return Math.max(o,Math.min(r,e))}function tc(e,o,r){return e+(o-e)*r}const Yy=256,wm=64;function Sj(e,o,r){if(o===r)return new Float32Array(e);const i=o/r,c=Math.max(1,Math.round(e.length/i)),l=new Float32Array(c);for(let p=0;p<c;p+=1){const f=p*i,b=Math.floor(f),y=Math.min(e.length-1,b+1),w=f-b;l[p]=e[b]*(1-w)+e[y]*w}return l}function xj(e,o,r=Qe.features.energyFrameMs,i=Qe.features.energyHopMs){const c=Math.max(1,Math.round(r/1e3*o)),l=Math.max(1,Math.round(i/1e3*o)),p=[];for(let b=0;b<e.length;b+=l){const y=e.subarray(b,Math.min(e.length,b+c));p.push(so(y))}const f=Math.max(...p,0);return f<=0?p.map(()=>0):p.map(b=>b/f)}function Rj(e,o=Qe.features.energyHopMs){if(e.length===0)return[];const r=Math.max(.08,Fh(e)*.42),i=[];let c=-1;return e.forEach((l,p)=>{if(l<r&&c<0&&(c=p),(l>=r||p===e.length-1)&&c>=0){const f=l>=r?p:p+1;f-c>=3&&i.push((c+f)/2*(o/1e3)),c=-1}}),i}function Pj(e,o=Qe.features.energyHopMs){const r=[],i=[],c=Math.max(.18,Fh(e)*.8);for(let l=1;l<e.length;l+=1){const p=e[l-1],f=e[l];p<c&&f>=c&&r.push(l*(o/1e3));const b=e[l+1]??0;f>c&&f>=p&&f>=b&&i.push(l*(o/1e3))}return{onsetTimes:r,peakTimes:i}}function Cj(e,o,r=Qe.features.pitchFrameMs,i=Qe.features.pitchHopMs){const c=Math.max(1,Math.round(r/1e3*o)),l=Math.max(1,Math.round(i/1e3*o)),p=[],f=[],b=Math.max(1,Math.floor(o/Qe.features.maxPitchHz)),y=Math.min(c-1,Math.ceil(o/Qe.features.minPitchHz));for(let w=0;w+c<=e.length;w+=l){const M=e.subarray(w,w+c);if(so(M)<.015){p.push(0),f.push(0);continue}let z=0,N=0,j=0;for(let D=0;D<M.length;D+=1)j+=M[D]*M[D];for(let D=b;D<=y;D+=1){let I=0;for(let L=0;L<M.length-D;L+=1)I+=M[L]*M[L+D];const B=j>0?I/j:0;B>N&&(N=B,z=D)}N>=Qe.features.pitchConfidenceThreshold&&z>0?(p.push(o/z),f.push(N)):(p.push(0),f.push(0))}return{contour:p,confidence:Fh(f)}}function Sm(e,o){const r=Sj(e,o,Qe.features.analysisSampleRate),i=Qe.features.analysisSampleRate,c=e.length/o,l=so(r),p=br(r);if(Mj(l,p))return jj(c,i,l,p);const f=xj(r,i),b=Rj(f),{onsetTimes:y,peakTimes:w}=Pj(f),M=Cj(r,i),E=f.filter(N=>N>.08).length,z=f.length>0?E/f.length:0;return{duration:c,voicedDuration:z*c,sampleRate:i,pitchContour:oc(Ky(M.contour,Yy),1),pitchConfidence:Number(M.confidence.toFixed(3)),energyEnvelope:oc(Ky(f,Yy),3),pausePositions:oc(xm(b,wm),3),onsetTimes:oc(xm(y,wm),3),peakTimes:oc(xm(w,wm),3),voicedRatio:Number(z.toFixed(3)),rms:Yd(l),peak:Yd(p)}}function Mj(e,o){return e<et(-50)&&o<et(-36)}function jj(e,o,r,i){return{duration:e,voicedDuration:0,sampleRate:o,pitchContour:[],pitchConfidence:0,energyEnvelope:[],pausePositions:[],onsetTimes:[],peakTimes:[],voicedRatio:0,rms:Yd(r),peak:Yd(i)}}function Yd(e){return!Number.isFinite(e)||e<0?e:Number(Math.min(1,e).toFixed(6))}function Fh(e){return e.length===0?0:e.reduce((o,r)=>o+r,0)/e.length}function oc(e,o){const r=10**o;return e.map(i=>Math.round(i*r)/r)}function Ky(e,o){return e.length<=o?e:e.length===1?[e[0]??0]:Array.from({length:o},(r,i)=>{const c=i/(o-1)*(e.length-1),l=Math.floor(c),p=Math.min(e.length-1,l+1),f=c-l;return e[l]*(1-f)+e[p]*f})}function xm(e,o){if(e.length<=o)return e;const r=e.length-1;return Array.from({length:o},(i,c)=>{const l=Math.round(c/Math.max(1,o-1)*r);return e[l]})}const Tj="modulepreload",Ej=function(e){return"/"+e},Zy={},Ko=function(o,r,i){let c=Promise.resolve();if(r&&r.length>0){let b=function(y){return Promise.all(y.map(w=>Promise.resolve(w).then(M=>({status:"fulfilled",value:M}),M=>({status:"rejected",reason:M}))))};document.getElementsByTagName("link");const p=document.querySelector("meta[property=csp-nonce]"),f=p?.nonce||p?.getAttribute("nonce");c=b(r.map(y=>{if(y=Ej(y),y in Zy)return;Zy[y]=!0;const w=y.endsWith(".css"),M=w?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${y}"]${M}`))return;const E=document.createElement("link");if(E.rel=w?"stylesheet":Tj,w||(E.as="script"),E.crossOrigin="",E.href=y,f&&E.setAttribute("nonce",f),document.head.appendChild(E),w)return new Promise((z,N)=>{E.addEventListener("load",z),E.addEventListener("error",()=>N(new Error(`Unable to preload CSS for ${y}`)))})}))}function l(p){const f=new Event("vite:preloadError",{cancelable:!0});if(f.payload=p,window.dispatchEvent(f),!f.defaultPrevented)throw p}return c.then(p=>{for(const f of p||[])f.status==="rejected"&&l(f.reason);return o().catch(l)})},Aw=[79,103,103,83],Aj=new TextEncoder().encode("OpusHead"),Ij=new Uint8Array([1,118,111,114,98,105,115]);function Nj(e){if(!Nw(e,Aw))return;const o=e.subarray(0,Math.min(e.length,65536));if(Xy(o,Aj))return"opus";if(Xy(o,Ij))return"vorbis"}function Dj(e,o=""){return Nw(e,Aw)||/(?:audio|application)\/(?:ogg|opus)/i.test(o)}async function Lj(e,o){if(e)return{bytes:await e.arrayBuffer(),mimeType:e.type};const r=await fetch(o);if(!r.ok)throw new Error("Reference audio unavailable");return{bytes:await r.arrayBuffer(),mimeType:r.headers.get("content-type")??""}}async function Iw(e,o=""){const r=globalThis.AudioContext??globalThis.webkitAudioContext;if(!r)throw new Error("Web Audio is unavailable in this browser.");const i=new r;try{const c=await hu(i,e,o);return Oh(c)}finally{await i.close().catch(()=>{})}}async function hu(e,o,r=""){try{return await e.decodeAudioData(o.slice(0))}catch(i){const c=new Uint8Array(o);if(!Dj(c,r))throw i;try{const l=await zj(c);return Fj(e,l)}catch(l){const p=lh(i),f=lh(l);throw new Error(`OGG fallback decoding failed (${f}). Native decoder: ${p}`)}}}function Vj(e,o){if(!e.length)return new Float32Array;const r=Math.max(0,Math.min(o??e[0].length,...e.map(c=>c.length))),i=new Float32Array(r);for(const c of e)for(let l=0;l<r;l+=1)i[l]+=c[l]/e.length;return i}async function zj(e){const o=Nj(e),r=o?[o]:["vorbis","opus"],i=[];for(const c of r)try{const l=c==="vorbis"?await Oj(e):await Uj(e);return Bj(l,c),l}catch(l){i.push(`${c}: ${lh(l)}`)}throw new Error(i.join("; ")||"Unsupported OGG codec")}async function Oj(e){const{OggVorbisDecoder:o}=await Ko(async()=>{const{OggVorbisDecoder:i}=await import("./index-BNkiwcKy.js");return{OggVorbisDecoder:i}},__vite__mapDeps([0,1,2])),r=new o;try{return await r.ready,await r.decodeFile(e)}finally{r.free()}}async function Uj(e){const{OggOpusDecoder:o}=await Ko(async()=>{const{OggOpusDecoder:i}=await import("./index-BRzZVyqk.js");return{OggOpusDecoder:i}},__vite__mapDeps([3,1,2,4])),r=new o;try{return await r.ready,await r.decodeFile(e)}finally{r.free()}}function Fj(e,o){const r=Vj(o.channelData,o.samplesDecoded),i=e.createBuffer(1,r.length,o.sampleRate),c=new Float32Array(r.length);
2return c.set(r),i.copyToChannel(c,0),i}function Bj(e,o){if(!Number.isFinite(e.sampleRate)||e.sampleRate<=0||e.samplesDecoded<=0||!e.channelData.length||e.channelData.every(r=>r.length===0))throw new Error(`${o} decoder returned no audio`)}function Nw(e,o){if(e.length<o.length)return!1;for(let r=0;r<o.length;r+=1)if(e[r]!==o[r])return!1;return!0}function Xy(e,o){if(!o.length||e.length<o.length)return!1;const r=e.length-o.length;for(let i=0;i<=r;i+=1){let c=!0;for(let l=0;l<o.length;l+=1)if(e[i+l]!==o[l]){c=!1;break}if(c)return!0}return!1}function lh(e){return e instanceof Error?e.message:String(e)}const Wj=600*1e3,Qy=128,Yo=new Map;async function Dw(e){if(!$j(e))return ev(e);const o=Date.now(),r=Yo.get(e);if(r&&r.expiresAt>o)return r.lastUsedAt=o,r.request;r&&Yo.delete(e);const i=ev(e).catch(c=>{throw Yo.get(e)?.request===i&&Yo.delete(e),c});return Yo.set(e,{expiresAt:o+Wj,lastUsedAt:o,request:i}),Gj(),i}function $j(e){try{return new URL(e,globalThis.location?.href??"https://voxalike.local").pathname.startsWith("/api/assets/")}catch{return!1}}async function ev(e){const o=await fetch(e);if(!o.ok)throw new Error("Audio unavailable");return{bytes:await o.arrayBuffer(),mimeType:o.headers.get("content-type")??""}}function Gj(){const e=Date.now();for(const[r,i]of Yo)i.expiresAt<=e&&Yo.delete(r);if(Yo.size<=Qy)return;[...Yo.entries()].sort((r,i)=>r[1].lastUsedAt-i[1].lastUsedAt).slice(0,Yo.size-Qy).forEach(([r])=>Yo.delete(r))}function Kd(e){if(!e||!e.active)return!1;const o=e.getAudioTracks()[0];return!!(o&&o.readyState==="live"&&o.enabled)}function qj(e){return Kd(e)?!e.getAudioTracks()[0]?.muted:!1}const rc="project-echo:last-audioinput",Hj=1e4;class Jj extends EventTarget{stream;devices=[];selectedDeviceId=localStorage.getItem(rc)??"default";status="idle";errorMessage;constructor(){super(),navigator.mediaDevices?.addEventListener("devicechange",this.handleDeviceChange)}getSnapshot(){return{status:this.status,devices:this.devices,selectedDeviceId:this.selectedDeviceId,stream:this.stream,settings:this.stream?.getAudioTracks()[0]?.getSettings(),errorMessage:this.errorMessage}}async requestPermission(o=this.selectedDeviceId,r="natural"){this.status="requesting",this.errorMessage=void 0,this.emit();try{this.stop();const i=await _j({audio:{...fj(r),deviceId:o&&o!=="default"?{exact:o}:void 0}},Hj);return this.stream=i,this.selectedDeviceId=o||"default",localStorage.setItem(rc,this.selectedDeviceId),this.status="ready",await this.refreshDevices(),this.watchTrack(i),this.emit(),this.getSnapshot()}catch(i){return this.status=Yj(i),this.errorMessage=i instanceof Error?i.message:"Microphone unavailable",this.status==="overconstrained"&&o!=="default"?(this.selectedDeviceId="default",localStorage.removeItem(rc),this.requestPermission("default",r)):(await this.refreshDevices(),this.emit(),this.getSnapshot())}}async selectDevice(o,r="natural"){return this.selectedDeviceId=o||"default",localStorage.setItem(rc,this.selectedDeviceId),this.requestPermission(this.selectedDeviceId,r)}async refreshDevices(){if(!navigator.mediaDevices?.enumerateDevices)return this.devices=[],this.devices;try{const r=await navigator.mediaDevices.enumerateDevices();this.devices=r.filter(i=>i.kind==="audioinput")}catch{return this.devices=[],this.devices}return this.selectedDeviceId==="default"||this.devices.some(r=>r.deviceId===this.selectedDeviceId)||(this.selectedDeviceId="default",localStorage.removeItem(rc),this.stream&&(this.stop(),this.status="disconnected",this.errorMessage="The selected microphone is no longer available.")),this.emit(),this.devices}stop(){this.stream?.getTracks().forEach(o=>o.stop()),this.stream=void 0}validateConnection(){return this.stream?Kd(this.stream)?!0:(this.stop(),this.status="disconnected",this.errorMessage="The microphone stopped while the app was in the background.",this.emit(),!1):!1}invalidateConnection(o="The microphone must be reconnected before recording again."){this.stop(),this.status="disconnected",this.errorMessage=o,this.emit()}destroy(){this.stop(),navigator.mediaDevices?.removeEventListener("devicechange",this.handleDeviceChange)}handleDeviceChange=()=>{this.refreshDevices()};watchTrack(o){const[r]=o.getAudioTracks();r&&(r.onended=()=>{this.stream===o&&(this.stream=void 0,this.status="disconnected",this.errorMessage="The selected microphone disconnected.",this.emit
2())},r.onmute=()=>{this.stream},r.onunmute=()=>{this.stream!==o||r.readyState!=="live"||(this.status="ready",this.errorMessage=void 0,this.emit())})}emit(){this.dispatchEvent(new Event("change"))}}function _j(e,o){let r;if(navigator.mediaDevices?.getUserMedia)r=navigator.mediaDevices.getUserMedia(e);else{const i=navigator,c=i.getUserMedia??i.webkitGetUserMedia??i.mozGetUserMedia;if(!c)return Promise.reject(new DOMException("Microphone access is not available in this browser.","NotSupportedError"));r=new Promise((l,p)=>{c.call(navigator,e,l,p)})}return new Promise((i,c)=>{let l=!1;const p=window.setTimeout(()=>{l=!0,c(new DOMException("Microphone connection timed out. Tap Retry Mic.","TimeoutError"))},o);r.then(f=>{if(l){f.getTracks().forEach(b=>b.stop());return}l=!0,window.clearTimeout(p),i(f)},f=>{l||(l=!0,window.clearTimeout(p),c(f))})})}function Yj(e){if(!(e instanceof DOMException))return"unavailable";switch(e.name){case"NotAllowedError":case"SecurityError":return"denied";case"NotFoundError":return"not-found";case"NotReadableError":return"busy";case"OverconstrainedError":return"overconstrained";default:return"unavailable"}}const Lw=1,Vw=.5,zw=3,Kj=.1,Zj=et(-1);function gu(e,o=Lw){return typeof e!="number"||!Number.isFinite(e)?o:Math.max(Vw,Math.min(zw,e))}function gc(e,o){const r=gu(o);if(e.samples.length===0||r===1)return Qj(e);if(r<1)return{samples:Pc(e.samples,r),sampleRate:e.sampleRate};const i=br(e.samples),c=i>0?Zj/i:r,l=Math.max(1,Math.min(r,c));return{samples:Pc(e.samples,l),sampleRate:e.sampleRate}}function Xj(e,o,r){return{scoringAudio:e,playbackAudio:gc(o,r)}}function Qj(e){return{samples:new Float32Array(e.samples),sampleRate:e.sampleRate}}const dh=.6;function Zd(e){return Math.max(0,Math.min(1,Number.isFinite(e)?e:dh))}function Rm(e,o){return Math.max(0,Math.min(1,Number.isFinite(e)?e:1))*Zd(o)}function uh(e,o){return tv(e)*tv(o)}function e0(e){return e.routeAllowsMusic&&uh(e.masterVolume,e.musicVolume)>0&&!e.microphoneConnecting&&!e.micCheckActive&&!e.recording&&e.pageVisible&&e.ownsPlayback}function t0(e,o){return e.activatedAt!==o.activatedAt?e.activatedAt-o.activatedAt:e.ownerId.localeCompare(o.ownerId)}function o0(e){if(!e||typeof e!="object")return!1;const o=e;return o.type==="background-music-claim"&&typeof o.ownerId=="string"&&o.ownerId.length>0&&typeof o.activatedAt=="number"&&Number.isFinite(o.activatedAt)}function tv(e){return Number.isFinite(e)?Math.max(0,Math.min(1,e)):0}const Mo={masterVolume:.5,sfxVolume:.3,musicVolume:.1,microphoneVolume:Lw},Ow="voxalike:audio-volume-preferences:v1";function ov(e){if(!e)return{...Mo};try{const o=e.getItem(Ow);if(!o)return{...Mo};const r=JSON.parse(o);return!r||typeof r!="object"?{...Mo}:{masterVolume:Ui(r.masterVolume,Mo.masterVolume),sfxVolume:Ui(r.sfxVolume,Mo.sfxVolume),musicVolume:Ui(r.musicVolume,Mo.musicVolume),microphoneVolume:gu(r.microphoneVolume,Mo.microphoneVolume)}}catch{return{...Mo}}}function r0(e,o){if(e)try{e.setItem(Ow,JSON.stringify({masterVolume:Ui(o.masterVolume,Mo.masterVolume),sfxVolume:Ui(o.sfxVolume,Mo.sfxVolume),musicVolume:Ui(o.musicVolume,Mo.musicVolume),microphoneVolume:gu(o.microphoneVolume,Mo.microphoneVolume)}))}catch{}}function Ui(e,o){return typeof e!="number"||!Number.isFinite(e)?o:Math.max(0,Math.min(1,e))}const a0=` 3class EchoPcmRecorder extends AudioWorkletProcessor { 4 process(inputs) { 5 const input = inputs[0]; 6 if (!input || input.length === 0 || !input[0]) { 7 return true; 8 } 9 10 const length = input[0].length; 11 const mono = new Float32Array(length); 12 13 for (let channel = 0; channel < input.length; channel += 1) { 14 const channelData = input[channel]; 15 for (let index = 0; index < length; index += 1) { 16 mono[index] += channelData[index] / input.length; 17 } 18 } 19 20 let sumSquares = 0; 21 let peak = 0; 22 for (let index = 0; index < mono.length; index += 1) { 23 const sample = mono[index]; 24 sumSquares += sample * sample; 25 peak = Math.max(peak, Math.abs(sample)); 26 } 27 28 this.port.postMessage( 29 { 30 type: "pcm", 31 samples: mono, 32 rms: Math.sqrt(sumSquares / Math.max(1, mono.length)), 33 peak 34 }, 35 [mono.buffer] 36 ); 37 38 return true; 39 } 40} 41 42registerProcessor("echo-pcm-recorder", EchoPcmRecorder); 43`;
43class Uw extends Error{constructor(o,r){super(o),this.details=r,this.name="RecordingFinalizeError"}details}const Fw=4e3,n0=500,i0=3e3,s0=2500,c0=1800,l0=250,d0=5e3;function rv(){try{const e=new AudioContext;return e.state!=="running"&&e.resume().catch(()=>{}),e}catch{return}}function u0(e,o="",r=0){return/iPhone|iPad|iPod/i.test(e)||o==="MacIntel"&&r>1}class av{constructor(o,r,i={}){this.stream=o,this.onLevel=r,this.options=i,this.audioContext=i.audioContext}stream;onLevel;options;audioContext;source;workletNode;mediaPcmProcessor;zeroGain;mediaRecorder;mediaChunks=[];chunks=[];workletUrl;fallbackActive=!1;recorderMode="unknown";startedAt=0;lastAudioDataAt=0;audioFrameCount=0;levelSampleCount=0;maxPeak=0;runtimeFailure;watchdogTimer;muteTimer;heartbeatExpected=!1;monitoredTrack;async start(){if(this.chunks=[],this.mediaChunks=[],this.fallbackActive=!1,this.recorderMode="unknown",this.startedAt=Date.now(),this.lastAudioDataAt=this.startedAt,this.audioFrameCount=0,this.levelSampleCount=0,this.maxPeak=0,this.runtimeFailure=void 0,this.attachTrackMonitoring(),this.audioContext??=new AudioContext,(this.options.preferMediaRecorder??u0(navigator.userAgent,navigator.platform,navigator.maxTouchPoints))&&"MediaRecorder"in window){this.options.onStartupStage?.("media-recorder"),this.recorderMode="media-recorder",await this.startMediaRecorderFallback(),this.startRuntimeWatchdog(),this.options.onStartupStage?.("recording");return}if(this.options.onStartupStage?.("audio-context"),await p0(this.audioContext)&&"audioWorklet"in this.audioContext){this.options.onStartupStage?.("audio-worklet");try{this.recorderMode="audio-worklet",await this.startWorkletRecorder(),this.heartbeatExpected=!0,this.startRuntimeWatchdog(),this.options.onStartupStage?.("recording");return}catch{this.resetWorkletRecorder()}}this.options.onStartupStage?.("media-recorder"),this.recorderMode="media-recorder",await this.startMediaRecorderFallback(),this.startRuntimeWatchdog(),this.options.onStartupStage?.("recording")}async stop(){if(this.stopRuntimeMonitoring(),this.mediaRecorder&&this.fallbackActive)return this.stopMediaRecorderFallback();this.workletNode?.disconnect(),this.source?.disconnect(),this.zeroGain?.disconnect();const o=this.audioContext?.sampleRate??48e3;return await this.audioContext?.close(),this.revokeWorkletUrl(),{samples:Jy(this.chunks),sampleRate:o,fallbackActive:!1,health:this.getHealth()}}cancel(){this.stopRuntimeMonitoring(),this.mediaRecorder?.state==="recording"&&this.mediaRecorder.stop(),this.workletNode?.disconnect(),this.stopMediaRecorderPcmBackup(),this.source?.disconnect(),this.zeroGain?.disconnect(),this.audioContext?.close(),this.revokeWorkletUrl()}async startWorkletRecorder(){if(!this.audioContext)throw new Error("AudioContext is not available");const o=new Blob([a0],{type:"text/javascript"});this.workletUrl=URL.createObjectURL(o),await Bw(this.audioContext.audioWorklet.addModule(this.workletUrl),Fw,"AudioWorklet startup timed out."),this.source=this.audioContext.createMediaStreamSource(this.stream),this.workletNode=new AudioWorkletNode(this.audioContext,"echo-pcm-recorder"),this.zeroGain=this.audioContext.createGain(),this.zeroGain.gain.value=0,this.workletNode.port.onmessage=r=>{const i=r.data;i.type==="pcm"&&(this.chunks.push(new Float32Array(i.samples)),this.noteAudioData(i.peak),this.onLevel?.({rms:i.rms,peak:i.peak}))},this.source.connect(this.workletNode),this.workletNode.connect(this.zeroGain),this.zeroGain.connect(this.audioContext.destination)}async startMediaRecorderFallback(){if(!this.audioContext)throw new Error("AudioContext is not available");if(!("MediaRecorder"in window))throw new Error("AudioWorklet and MediaRecorder are unavailable");this.fallbackActive=!0,this.mediaRecorder=new MediaRecorder(this.stream),this.mediaRecorder.ondataavailable=o=>{this.noteAudioData(),o.data.size>0&&this.mediaChunks.push(o.data)},this.mediaRecorder.onerror=o=>{const r=o.error;this.triggerRuntimeFailure("media-recorder-error",r?.message||"The mobile recorder reported an error.")},this.mediaRecorder.start(500),this.audioContext.state==="running"?this.startMediaRecorderPcmBackup():this.audioContext.resume().then(()=>this.startMediaRecorderPcmBackup(),()=>{})}async stopMediaRecorderFallback(){const o=this.mediaRecorder,r=this.audioContext;if(!o||!r)throw new Error("Fallback recorder is not active");this.fallbackActive=!1;const i=this.stopMediaRecorderPcmBackup();o.state==="recording"&&await this.flushMediaRecorder(o);const c=o.mimeType||this.mediaChunks[0]?.type||"audio/webm";return new Promise((l,p)=>{let f=!1;const b=window.setTimeout(()=>{p(this.createFinalizeError("The mobile recorder did not finish the audio file.","stop",c,o))},d0),y=async()=>
43{if(!f){f=!0,window.clearTimeout(b);try{const w=new Blob(this.mediaChunks,{type:c});if(w.size===0)throw this.createFinalizeError("The mobile recorder returned an empty audio file.","empty",c,o);const M=await w.arrayBuffer();let E;try{const N=await hu(r,M,w.type);E=Oh(N)}catch(N){if(i.samples.length>0)E=i;else throw this.createFinalizeError(`The browser could not decode the recorded audio: ${nv(N)}`,"decode",w.type,o)}const z=br(E.samples);this.maxPeak=Math.max(this.maxPeak,z),this.levelSampleCount+=1,this.source?.disconnect(),await r.close().catch(()=>{}),l({...E,fallbackActive:!0,health:this.getHealth()})}catch(w){this.source?.disconnect(),await r.close().catch(()=>{}),p(w instanceof Error?w:new Error("Failed to finalize mobile recording"))}}};if(o.onstop=()=>{y()},o.state==="inactive"){y();return}try{o.stop()}catch(w){window.clearTimeout(b),p(this.createFinalizeError(`The mobile recorder could not stop: ${nv(w)}`,"stop",c,o))}})}async flushMediaRecorder(o){try{await new Promise(r=>{let i=!1;const c=()=>{i||(i=!0,window.clearTimeout(l),o.removeEventListener("dataavailable",c),r())},l=window.setTimeout(c,l0);o.addEventListener("dataavailable",c,{once:!0}),o.requestData()})}catch{}}startMediaRecorderPcmBackup(){if(!(!this.audioContext||this.audioContext.state!=="running"||this.mediaPcmProcessor||!this.fallbackActive||this.mediaRecorder?.state!=="recording"))try{this.source=this.audioContext.createMediaStreamSource(this.stream),this.mediaPcmProcessor=this.audioContext.createScriptProcessor(2048,1,1),this.zeroGain=this.audioContext.createGain(),this.zeroGain.gain.value=0,this.mediaPcmProcessor.onaudioprocess=o=>{if(!this.fallbackActive||this.mediaRecorder?.state!=="recording")return;const r=o.inputBuffer,i=m0(r);this.chunks.push(i);const c=br(i);this.noteAudioData(c),this.onLevel?.({rms:so(i),peak:c})},this.source.connect(this.mediaPcmProcessor),this.mediaPcmProcessor.connect(this.zeroGain),this.zeroGain.connect(this.audioContext.destination)}catch{this.stopMediaRecorderPcmBackup()}}stopMediaRecorderPcmBackup(){const o=this.audioContext?.sampleRate??48e3;this.mediaPcmProcessor?.disconnect(),this.mediaPcmProcessor&&(this.mediaPcmProcessor.onaudioprocess=null),this.source?.disconnect(),this.zeroGain?.disconnect(),this.mediaPcmProcessor=void 0,this.source=void 0,this.zeroGain=void 0;const r=Jy(this.chunks);return this.chunks=[],{samples:r,sampleRate:o}}createFinalizeError(o,r,i,c){return new Uw(o,{stage:r,mimeType:i||"unknown",chunkCount:this.mediaChunks.length,byteLength:this.mediaChunks.reduce((l,p)=>l+p.size,0),recorderState:c.state,recorderMode:this.recorderMode})}revokeWorkletUrl(){this.workletUrl&&(URL.revokeObjectURL(this.workletUrl),this.workletUrl=void 0)}resetWorkletRecorder(){this.workletNode?.disconnect(),this.source?.disconnect(),this.zeroGain?.disconnect(),this.workletNode=void 0,this.source=void 0,this.zeroGain=void 0,this.revokeWorkletUrl()}attachTrackMonitoring(){this.monitoredTrack=this.stream.getAudioTracks()[0],this.monitoredTrack?.addEventListener("ended",this.handleTrackEnded),this.monitoredTrack?.addEventListener("mute",this.handleTrackMuted),this.monitoredTrack?.addEventListener("unmute",this.handleTrackUnmuted)}handleTrackEnded=()=>{this.triggerRuntimeFailure("track-ended","The phone stopped the microphone track while recording.")};handleTrackMuted=()=>{this.muteTimer!==void 0&&window.clearTimeout(this.muteTimer),this.muteTimer=window.setTimeout(()=>{this.monitoredTrack?.muted&&document.visibilityState==="visible"&&this.triggerRuntimeFailure("track-muted","The phone muted the microphone track while recording.")},c0)};handleTrackUnmuted=()=>{this.muteTimer!==void 0&&(window.clearTimeout(this.muteTimer),this.muteTimer=void 0),this.lastAudioDataAt=Date.now()};noteAudioData(o){this.lastAudioDataAt=Date.now(),this.heartbeatExpected=!0,this.audioFrameCount+=1,typeof o=="number"&&Number.isFinite(o)&&(this.levelSampleCount+=1,this.maxPeak=Math.max(this.maxPeak,o))}startRuntimeWatchdog(){this.watchdogTimer!==void 0&&window.clearInterval(this.watchdogTimer),this.watchdogTimer=window.setInterval(()=>{if(!(this.runtimeFailure||!this.heartbeatExpected||document.visibilityState!=="visible"||Date.now()-this.startedAt<i0)&&Date.now()-this.lastAudioDataAt>s0){this.triggerRuntimeFailure("audio-data-stalled","The browser stopped delivering microphone audio while recording.");return}},n0)}triggerRuntimeFailure(o,r){this.runtimeFailure||(this.runtimeFailure=this.createRu
43ntimeFailure(o,r),this.options.onRuntimeFailure?.(this.runtimeFailure))}createRuntimeFailure(o,r){const i=Date.now();return{code:o,message:r,elapsedMs:Math.max(0,i-this.startedAt),lastAudioDataAgeMs:Math.max(0,i-this.lastAudioDataAt),audioFrameCount:this.audioFrameCount,levelSampleCount:this.levelSampleCount,maxPeak:this.maxPeak,trackReadyState:this.monitoredTrack?.readyState??"missing",trackEnabled:this.monitoredTrack?.enabled??!1,trackMuted:this.monitoredTrack?.muted??!1,audioContextState:this.audioContext?.state??"missing",recorderMode:this.recorderMode}}getHealth(){return{audioFrameCount:this.audioFrameCount,levelSampleCount:this.levelSampleCount,maxPeak:this.maxPeak,runtimeFailure:this.runtimeFailure,recorderMode:this.recorderMode}}stopRuntimeMonitoring(){this.watchdogTimer!==void 0&&(window.clearInterval(this.watchdogTimer),this.watchdogTimer=void 0),this.muteTimer!==void 0&&(window.clearTimeout(this.muteTimer),this.muteTimer=void 0),this.monitoredTrack?.removeEventListener("ended",this.handleTrackEnded),this.monitoredTrack?.removeEventListener("mute",this.handleTrackMuted),this.monitoredTrack?.removeEventListener("unmute",this.handleTrackUnmuted),this.monitoredTrack=void 0}}async function p0(e){if(e.state==="running")return!0;try{await Bw(e.resume(),Fw,"The browser did not resume microphone audio.")}catch{return!1}return String(e.state)==="running"}async function Bw(e,o,r){let i;try{return await Promise.race([e,new Promise((c,l)=>{i=window.setTimeout(()=>l(new Error(r)),o)})])}finally{i!==void 0&&window.clearTimeout(i)}}function nv(e){return e instanceof Error?e.message:String(e)}function m0(e){const o=Math.max(1,e.numberOfChannels),r=new Float32Array(e.length);for(let i=0;i<o;i+=1){const c=e.getChannelData(i);for(let l=0;l<r.length;l+=1)r[l]+=c[l]/o}return r}const Ww=-500,$w=500,ph=10;function fu(e){if(!Number.isFinite(e))return 0;const o=Math.round(e/ph)*ph;return Math.max(Ww,Math.min($w,o))}function h0(e,o){const r=fu(o),i=Math.round(Math.abs(r)/1e3*e.sampleRate);if(i===0)return e;if(r<0)return{samples:e.samples.slice(Math.min(i,e.samples.length)),sampleRate:e.sampleRate};const c=new Float32Array(e.samples.length+i);return c.set(e.samples,i),{samples:c,sampleRate:e.sampleRate}}function En({samples:e,sampleRate:o}){const c=e.length*2,l=new ArrayBuffer(44+c),p=new DataView(l);Ja(p,0,"RIFF"),p.setUint32(4,36+c,!0),Ja(p,8,"WAVE"),Ja(p,12,"fmt "),p.setUint32(16,16,!0),p.setUint16(20,1,!0),p.setUint16(22,1,!0),p.setUint32(24,o,!0),p.setUint32(28,o*2,!0),p.setUint16(32,2,!0),p.setUint16(34,16,!0),Ja(p,36,"data"),p.setUint32(40,c,!0);let f=44;for(const b of e){const y=Math.max(-1,Math.min(1,b));p.setInt16(f,y<0?y*32768:y*32767,!0),f+=2}return new Blob([l],{type:"audio/wav"})}function Tn(e){return URL.createObjectURL(En(e))}function g0(e,o,r){const i=Math.min(e.length,o.length),c=2,l=2,p=c*l,f=i*p,b=new ArrayBuffer(44+f),y=new DataView(b);Ja(y,0,"RIFF"),y.setUint32(4,36+f,!0),Ja(y,8,"WAVE"),Ja(y,12,"fmt "),y.setUint32(16,16,!0),y.setUint16(20,1,!0),y.setUint16(22,c,!0),y.setUint32(24,r,!0),y.setUint32(28,r*p,!0),y.setUint16(32,p,!0),y.setUint16(34,16,!0),Ja(y,36,"data"),y.setUint32(40,f,!0);for(let w=0;w<i;w+=1)y.setInt16(44+w*p,iv(e[w]??0),!0),y.setInt16(44+w*p+l,iv(o[w]??0),!0);return new Blob([b],{type:"audio/wav"})}function iv(e){const o=Math.max(-1,Math.min(1,e));return Math.round(o<0?o*32768:o*32767)}function Ja(e,o,r){for(let i=0;i<r.length;i+=1)e.setUint8(o+i,r.charCodeAt(i))}function f0({rms:e,peak:o,label:r}){const i=Math.min(100,Math.max(0,e*160)),c=Math.min(100,Math.max(0,o*100));return t.jsxs("div",{className:"level-block",children:[t.jsxs("div",{className:"level-heading",children:[t.jsx("span",{children:r}),t.jsx("span",{children:Number.isFinite(ea(e))?`${ea(e).toFixed(1)} dBFS`:"-inf dBFS"})]}),t.jsxs("div",{className:"meter-track","aria-label":`${r} level`,children:[t.jsx("div",{className:"meter-rms",style:{width:`${i}%`}}),t.jsx("div",{className:"meter-peak",style:{left:`${c}%`}})]})]})}const b0="voxalike-dub-drafts",y0=1,jo="drafts",yr="takes",Xd="draftId",Bh=10080*60*1e3,v0=3,k0=5;function w0(e,o){return e.mode==="single"&&e.status==="active"&&e.id!==o}async function S0(e){const o=Date.now(),r={...e,schemaVersion:1,
43createdAt:e.createdAt??o,updatedAt:e.updatedAt??o},i=await On();try{const c=i.transaction(jo,"readwrite");c.objectStore(jo).put(r),await Un(c)}finally{i.close()}return await R0(),r}async function ac(e,o){const r=await On();try{const i=r.transaction(jo,"readwrite"),c=i.objectStore(jo),l=await Ic(c.get(e));if(!l){i.abort();return}const p={...l,...o,updatedAt:Date.now()};return c.put(p),await Un(i),p}finally{r.close()}}async function sv(e,o){const r=await On();try{const i=r.transaction([jo,yr],"readwrite"),c=i.objectStore(jo),l=await Ic(c.get(e));if(!l)throw i.abort(),new Error("Dub draft no longer exists.");const p=Date.now();i.objectStore(yr).put({...o,key:Gw(e,o.takeKey),draftId:e,savedAt:p}),c.put({...l,updatedAt:p}),await Un(i)}finally{r.close()}}async function Od(){if(!qw())return[];const e=await On();try{const o=e.transaction(jo,"readonly"),r=await Ic(o.objectStore(jo).getAll());return await Un(o),r.filter(i=>i.schemaVersion===1).sort((i,c)=>c.updatedAt-i.updatedAt)}finally{e.close()}}async function fc(e){const o=await On();try{const r=o.transaction(yr,"readonly"),i=r.objectStore(yr).index(Xd),c=await Ic(i.getAll(e));return await Un(r),c.sort((l,p)=>l.savedAt-p.savedAt)}finally{o.close()}}async function x0(e,o){const r=await On();try{const i=r.transaction(yr,"readwrite");i.objectStore(yr).delete(Gw(e,o)),await Un(i)}finally{r.close()}}async function Sc(e){const o=await On();try{const r=o.transaction([jo,yr],"readwrite");r.objectStore(jo).delete(e);const i=r.objectStore(yr);(await Ic(i.index(Xd).getAllKeys(e))).forEach(l=>i.delete(l)),await Un(r)}finally{o.close()}}async function R0(e=Date.now()){const o=await Od(),r=new Map,i=o.filter(c=>{if(e-c.updatedAt>Bh)return!0;const l=r.get(c.mode)??0;r.set(c.mode,l+1);const p=c.mode==="multi"?k0:v0;return l>=p});await Promise.all(i.map(c=>Sc(c.id)))}function Gw(e,o){return`${e}:${o}`}function qw(){return typeof indexedDB<"u"}function On(){return qw()?new Promise((e,o)=>{const r=indexedDB.open(b0,y0);r.onupgradeneeded=()=>{const i=r.result;i.objectStoreNames.contains(jo)||i.createObjectStore(jo,{keyPath:"id"}),i.objectStoreNames.contains(yr)||i.createObjectStore(yr,{keyPath:"key"}).createIndex(Xd,Xd,{unique:!1})},r.onsuccess=()=>e(r.result),r.onerror=()=>o(r.error??new Error("Browser storage failed.")),r.onblocked=()=>o(new Error("Browser storage is blocked by another tab."))}):Promise.reject(new Error("Browser storage is unavailable."))}function Ic(e){return new Promise((o,r)=>{e.onsuccess=()=>o(e.result),e.onerror=()=>r(e.error??new Error("Browser storage failed."))})}function Un(e){return new Promise((o,r)=>{e.oncomplete=()=>o(),e.onerror=()=>r(e.error??new Error("Browser storage failed.")),e.onabort=()=>r(e.error??new Error("Browser storage was aborted."))})}const Nc="voxalike-dub-draft-recovery",Hw=80*1024*1024,P0=3e6,Qd=500,Jw=80,cv=88;async function _w(e,o){const{default:r}=await Ko(async()=>{const{default:f}=await import("./jszip.min-CK9NPRCS.js").then(b=>b.j);return{default:f}},__vite__mapDeps([5,2,6])),i=new r,c=j0(e.roomGame);if(c)return M0(i,e,o,c);const l=[];for(const[f,b]of o.entries()){const y=Xw(f,b.clipId||b.takeKey);i.file(`recordings/${y}`,await b.audioBlob.arrayBuffer(),{compression:"STORE"}),l.push({filename:y,clipId:b.clipId,savedAt:new Date(b.savedAt).toISOString()})}const p={format:Nc,version:1,roomCode:e.roomCode,packTitle:e.packTitle,draftStatus:e.status,createdAt:new Date(e.createdAt).toISOString(),updatedAt:new Date(e.updatedAt).toISOString(),recordings:l};return i.file("recovery-info.json",JSON.stringify(p,null,2)),i.generateAsync({type:"blob",mimeType:"application/zip",compression:"STORE"})}async function C0(e){if(e.size>Hw)throw new Error("Recovery ZIP is too large. The maximum supported size is 80 MB.");const{default:o}=await Ko(async()=>{const{default:y}=await import("./jszip.min-CK9NPRCS.js").then(w=>
43w.j);return{default:y}},__vite__mapDeps([5,2,6])),r=await o.loadAsync(await e.arrayBuffer(),{checkCRC32:!0,createFolders:!1}),i=Object.values(r.files);if(i.length>Qd+4||i.some(y=>I0(y.name)))throw new Error("Recovery ZIP contains an unsafe or unsupported file layout.");const c=r.file("recovery-info.json");if(!c)throw new Error("This is not a Voxalike Recovery ZIP.");let l;try{l=JSON.parse(await c.async("text"))}catch{throw new Error("Recovery ZIP information is damaged.")}if(T0(l)){const y=E0(l),w=[];let M=0;for(const E of y.recordings){const z=await lv(r,E.filename,M);M+=z.size,w.push({clipId:E.clipId,savedAt:E.savedAt,audioBlob:z})}return{kind:"legacy-recordings",manifest:y,recordings:w}}const p=A0(l),f=[];let b=0;for(const y of p.recordings){const w=await lv(r,y.filename,b);b+=w.size,f.push({take:Yw(y),audioBlob:w})}return{kind:"room",manifest:p,game:p.game,recordings:f}}async function M0(e,o,r,i){const c=new Map(r.filter(b=>Number.isFinite(b.assignmentIndex)).map(b=>[Math.round(Number(b.assignmentIndex)),b])),l=[];for(const b of i.takes){const y=c.get(b.assignmentIndex);if(!y)throw new Error("Some room recordings have not finished saving to this browser yet. Keep the room open and try again.");const w=Xw(b.assignmentIndex,b.clipId);e.file(`recordings/${w}`,await y.audioBlob.arrayBuffer(),{compression:"STORE"}),l.push({...Yw(b),filename:w,takeKey:y.takeKey,savedAt:new Date(y.savedAt).toISOString()})}const p=D0(i),f={format:Nc,version:2,roomCode:o.roomCode,packTitle:o.packTitle,draftStatus:o.status,sourceId:o.sourceId,packId:o.packId,currentClipIndex:o.currentClipIndex,clipSequence:o.clipSequence,roomRunKey:o.roomRunKey,createdAt:new Date(o.createdAt).toISOString(),updatedAt:new Date(o.updatedAt).toISOString(),game:p,recordings:l};return e.file("recovery-info.json",JSON.stringify(f,null,2)),e.generateAsync({type:"blob",mimeType:"application/zip",compression:"STORE"})}function j0(e){return e?.mode==="coop"&&e.assignmentMode==="claim"&&!e.completedAt?e:void 0}function T0(e){const o=e;return!!(o&&o.format===Nc&&o.version===1)}function E0(e){const o=e;if(!o||o.format!==Nc||o.version!==1)throw new Error("This Recovery ZIP version is not supported.");if(typeof o.packTitle!="string"||!o.packTitle.trim()||o.draftStatus!=="active"&&o.draftStatus!=="complete"||typeof o.createdAt!="string"||typeof o.updatedAt!="string"||!Array.isArray(o.recordings)||!o.recordings.length||o.recordings.length>Qd)throw new Error("Recovery recording information is missing or invalid.");const r=new Set;for(const i of o.recordings){if(!i||typeof i.filename!="string"||!Qw(i.filename)||r.has(i.filename)||typeof i.clipId!="string"||!i.clipId.trim()||typeof i.savedAt!="string")throw new Error("Recovery recording metadata is invalid.");r.add(i.filename)}return o}function A0(e){const o=e;if(!o||o.format!==Nc||o.version!==2)throw new Error("This Recovery ZIP version is not supported.");const r=o.game;if(!r||r.mode!=="coop"||r.assignmentMode!=="claim"||r.completedAt)throw new Error("Only interrupted Co-op Claim Mode rooms can be resumed.");if(!Array.isArray(r.assignments)||!r.assignments.length||r.assignments.length>Qd)throw new Error("Recovery room assignments are missing or invalid.");if(!Array.isArray(o.recordings)||o.recordings.length>Qd)throw new Error("Recovery recording list is invalid.");const i=new Set;for(const c of o.recordings){if(!c||typeof c.filename!="string"||!Qw(c.filename)||i.has(c.filename)||!Number.isInteger(c.assignmentIndex)||c.assignmentIndex<0||c.assignmentIndex>=r.assignments.length||r.assignments[c.assignmentIndex]?.clipId!==c.clipId)throw new Error("Recovery recording metadata is invalid.");i.add(c.filename)}return o}function I0(e){return e.includes("\\")||e.startsWith("/")||e.split("/").some(o=>o===".."||o===".")}function N0(e){return e.length>=12&&String.fromCharCode(...e.slice(0,4))==="RIFF"&&String.fromCharCode(...e.slice(8,12))==="WAVE"}async function lv(e,o,r){const i=e.file(`recordings/${o}`);if(!i)throw new Error(`Recovery recording is missing: ${o}`);const c=await i.async("uint8array");if(!c.byteLength||c.byteLength>P0||r+c.byteLength>Hw)throw new Error("A recovery recording is empty or too large.");if(!N0(c))throw new Error(`Recovery recording is not a valid WAV file: ${o}`);const l=c.buffer.slice(c.byteOffset,c.byteOffset+c.byteLength);return new Blob([l],{type:"audio/wav"})}function Yw(e){return{assignmentIndex:e.assignmentIndex,clipId:e.clipI
43d,playerId:e.playerId,playerName:e.playerName,score:e.score,scoreTotal:e.scoreTotal,rating:e.rating,createdAt:e.createdAt}}function D0(e){return{packId:e.packId,packTitle:e.packTitle,packSource:e.packSource,workshopPackId:e.workshopPackId,workshopPackSlug:e.workshopPackSlug,clipCount:e.clipCount,mode:e.mode,pvpFormat:e.pvpFormat,assignmentMode:e.assignmentMode,randomizePlayerOrder:e.randomizePlayerOrder,allowRetakes:e.allowRetakes,currentAssignmentIndex:e.currentAssignmentIndex,assignments:e.assignments,startedAt:e.startedAt,completedAt:e.completedAt,lastRedoAt:e.lastRedoAt,lastRedoAssignmentIndex:e.lastRedoAssignmentIndex,withdrawnPlayerIds:e.withdrawnPlayerIds}}function Kw(e){const o=V0(e.roomCode??"multiplayer","multiplayer").toUpperCase(),r=new Date(e.updatedAt).toISOString().slice(0,10);return`voxalike-recovery-${o}-${r}.zip`}async function Zw(e,o){if(L0()&&typeof navigator.share=="function"){const c=new File([e],o,{type:"application/zip"});try{if(typeof navigator.canShare=="function"&&navigator.canShare({files:[c]}))return await navigator.share({files:[c]}),"saved"}catch(l){if(l instanceof Error&&l.name==="AbortError")return"cancelled"}}const r=URL.createObjectURL(e),i=document.createElement("a");return i.href=r,i.download=o,document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(r),300*1e3),"saved"}function L0(){return/iPad|iPhone|iPod/.test(navigator.userAgent)||navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1}function V0(e,o){return Wh(e,o,Jw)}function Wh(e,o,r){return e.normalize("NFKD").split("").map(c=>c.charCodeAt(0)<32?"-":c).join("").replace(/[<>:"/\\|?*]/g,"-").replace(/\s+/g,"-").replace(/-+/g,"-").replace(/^[-.]+|[-.]+$/g,"").slice(0,r)||o}function Xw(e,o){const r=`${String(e+1).padStart(3,"0")}-`,i=".wav",c=Jw-r.length-i.length;return`${r}${Wh(o,"recording",c)}${i}`}function Qw(e){return e.length<=cv&&e.toLowerCase().endsWith(".wav")&&e===Wh(e,"",cv)}function z0({drafts:e,onDelete:o,onImport:r}){const[i,c]=m.useState([]),[l,p]=m.useState(),[f,b]=m.useState(),[y,w]=m.useState(!1),M=m.useRef(null);if(m.useEffect(()=>{let j=!1;const D=e.filter(I=>Date.now()-I.updatedAt<=Bh);return Promise.all(D.map(async I=>{const B=await fc(I.id);return{draft:I,takes:B,bytes:B.reduce((L,_)=>L+_.audioBlob.size,0)}})).then(I=>{j||c(I.filter(B=>B.takes.length>0))}).catch(()=>{j||b("Saved recordings could not be read from this browser.")}),()=>{j=!0}},[e]),m.useEffect(()=>{if(!y)return;const j=D=>{D.key==="Escape"&&!l&&w(!1)};return window.addEventListener("keydown",j),()=>window.removeEventListener("keydown",j)},[l,y]),!i.length&&!f&&!r)return null;async function E(j){if(!(!j||!r||l)){p("__import__"),b("Checking Recovery ZIP.");try{await r(j),b("Recovery loaded.")}catch(D){b(D instanceof Error?D.message:"Recovery ZIP could not be imported.")}finally{p(void 0),M.current&&(M.current.value="")}}}async function z(j){if(!l){p(j.draft.id),b("Preparing saved recordings. Keep this page open.");try{const D=await _w(j.draft,j.takes),I=await Zw(D,Kw(j.draft));b(I==="cancelled"?"Save cancelled. The recordings are still stored in this browser.":"Recovery ZIP saved. Keep it somewhere safe before deleting the draft.")}catch{b("The recovery ZIP could not be created. The original browser draft has not been deleted.")}finally{p(void 0)}}}async function N(j){if(!(l||!window.confirm("Permanently delete these locally saved recordings?"))){p(j.draft.id);try{await Sc(j.draft.id),c(D=>D.filter(I=>I.draft.id!==j.draft.id)),o(j.draft.id),b("Saved recordings deleted from this browser.")}catch{b("Saved recordings could not be deleted.")}finally{p(void 0)}}}return t.jsxs(t.Fragment,{children:[t.jsxs("button",{className:"ghost-button interrupted-dubs-trigger",type:"button",onClick:()=>w(!0),children:[t.jsxs("span",{children:[t.jsx(it,{size:16})," Interrupted Dubs"]}),i.length?t.jsx("strong",{"aria-label":`${i.length} saved dubs`,children:i.length}):null]}),y?t.jsx("div",{className:"interrupted-dubs-backdrop",role:"presentation",onClick:()=>!l&&w(!1),children:t.jsxs("section",{className:"panel interrupted-dubs-panel interrupted-dubs-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"interrupted-dubs-title",onClick:j=>j.stopPropagation(),children:[t.jsxs("div",{className:"panel-header interrupted-dubs-dialog-header",children:[t.jsxs("div",{children:[t.jsxs("span",{children:[t.jsx(it,{size:13})," INTERRUPTED DUBS"]}),t.jsx("h2",{id:"interrupted-dubs-title",children:"Recover Recordings"}),t.jsx("p",{children:"Recordings saved on this device remain available for up to 7 days."})]}),t.jsx("button",{className:"icon-button",type:"button",disabled:!!l,onClick:()=>w(!1),"aria-label":"Close interrupted dubs",title:"Close",children:t.jsx(Tt,{size:18})})]}),t.jsxs("div",{className:"panel-body recent-dubs-list","aria-live":"polite",children:[r?t.jsxs("div",{className:"recovery-zip-import",children:[t.jsx("input",{ref:M,type:"file",accept:".zip,application/zip",hidden:!0,onChange:j=>{E(j.currentTarget.files?.[0])}}),t.jsxs("button",{className:"action-button",type:"button",disabled:!!l,onClick:()=>M.current?.click(),children:[l==="__import__"?t.jsx(ky,{className:"spinning",size:16}):t.jsx(oh,{size:16}),l==="__import__"?"Restoring...":"Import Recovery ZIP"]}),t.jsx("small",{children:"Completed recordings or Co-op Claim Mode"})]}):null,i.map(j=>{const D=l===j.draft.id;return t.jsxs("article",{className:"recent-dub-card interrupted-dub-card",children:[t.jsxs("header",{children:[t.jsx("h3",{children:j.draft.packTitle}),t.jsx("time",{dateTime:new Date(j.draft.updatedAt).toISOString(),children:O0(j.draft.updatedAt)})]}),t.jsxs("p",{children:[j.draft.roomCode?`Room ${j.draft.roomCode} · `:"",j.takes.length," ",j.takes.length===1?"recording":"recordings"," · ",U0(j.bytes)]}),t.jsxs("footer",{children:[t.jsx("button",{className:"ghost-button icon-button interrupted-dub-delete",type:"button",disabled:!!l,onClick:()=>{N(j)},"aria-label":"Delete saved recordings",title:"Delete saved recordings",children:t.jsx(Ho,{size:16})}),t.jsxs("button",{className:"ghost-button",type:"button",disabled:!!l,onClick:()=>{z(j)},children:[D?t.jsx(ky,{className:"spinning",size:16}):t.jsx(Gt,{size:16}),D?"Preparing...":"Download ZIP"]})]})]},j.draft.id)}),f?t.jsx("p",{className:"interrupted-dub-notice",role:"status",children:f}):null]})]})}):null]})}function O0(e){const o=Math.floor(Math.max(0,Date.now()-e)/6e4);if(o<1)return"just now";if(o<60)return`${o}m ago`;const r=Math.floor(o/60);return r<24?`${r}h ago`:`${Math.floor(r/24)}d ago`}function U0(e){return e<1024*1024?`${Math.max(1,Math.round(e/1024))} KB`:`${(e/(1024*1024)).toFixed(e<10*1024*1024?1:0)} MB`}const dv="voxalike_guest_player_name_v1",eS=24,Pm=["rabbit","comet","pixel","echo","cactus","muffin","rocket","noodle","otter","panda","waffle","spark"];function F0(e=G0()){const o=e?.getItem(dv)?.trim();if(o&&o.length<=eS)return o;const r=B0();try{e?.setItem(dv,r)}catch{}return r}function B0(e=$0){const o=Pm[e(Pm.length)%Pm.length],r=100+e(900)%900;return`${o}_${r}`}function W0(e,o){const r=e?.trim();return tS(r||o)}function tS(e){return Array.from(e).slice(0,eS).join("")}function $0(e){return e<=1?0:globalThis.crypto?.getRandomValues?globalThis.crypto.getRandomValues(new Uint32Array(1))[0]%e:Math.floor(Math.random()*e)}function G0(){try{return typeof window>"u"?void 0:window.sessionStorage}catch{return}}const q0=["Harassment / Abuse","Hate Speech","Spam","Inappropriate Content","Other"],oS=["cyan","mint","sky","gold","violet","rose"],rS=["orbit","prism","film","aura"],uv={none:"No frame",orbit:"Original",prism:"Anime Girl",film:"Banana Cat",aura:"Violet Cloud"},H0="cyan",J0="none";function _0(e){return oS.includes(e)?e:H0}function Y0(e){return e==="none"||rS.includes(e)?e:J0}function aS(e){return`supporter-color-${_0(e)}`}function eu(e){const o=Y0(e);return o==="none"?"":`supporter-frame-art supporter-frame-${o}`}function tu({identity:e,compact:o=!1}){const r=e==="community_admin",i=e==="community_contributor",c=e==="mod_creator",l=r?"Voxalike Community Admin":i?"Voxalike Community Contributor":c?"Voxalike Mod Creator":"Voxalike Supporter";return t.jsx("span",{className:`supporter-badge ${e} ${o?"compact":""}`,title:l,"aria-label":l,children:r?t.jsx(nw,{size:o?11:13,"aria-hidden":"true"}):i?t.jsx(VC,{size:o?11:13,"aria-hidden":"true"}):c?t.jsx(zC,{size:o?11:13,"aria-hidden":"true"}):t.jsx("img",{src:"/supporter-badge-v2.svg",alt:"",draggable:!1})})}function _o({name:e,identity:o,nameColor:r,showBadge:i=!0}){const c=o==="community_admin"?"Community Admin":o==="community_contributor"?"Community Contributor":o==="mod_creator"?"Mod Creator":o==="supporter"?"Supporter":"",l=r?aS(r):"";return t.jsxs("span",{className:`supporter-name ${o??""} ${l}`,title:c?`${e} · ${c}`:e,children:[t.jsx("span",{children:e}),o&&i?t.jsx(tu,{identity:o}):null]})}function Fi(...e){const o=new Map;for(const r of e)for(const i of r??[]){const c=i.trim(),l=c.toLowerCase();c&&!o.has(l)&&o.set(l,c)}return[...o.values()]}function K0({playerName:e,roomCodeInput:o,roomMessage:r,packSelector:i,recentDubs:c,connectionPending:l,visibility:p,roomLanguage:f,signedIn:b,publicRooms:y,publicRoomsLoading:w,publicRoomsError:M,publicLanguageFilter:E,publicStatusFilter:z,onPlayerNameChange:N,onRoomCodeChange:j,onCreateRoom:D,onJoinRoom:I,onJoinPublicRoom:B,onVisibilityChange:L,onRoomLanguageChange:_,onPublicLanguageFilterChange:oe,onPublicStatusFilterChange:W,onRefreshPublicRooms:O,onSignIn:K}){const{t:T}=Be(),Y=y.filter(X=>X.visibility==="public");return t.jsxs("div",{className:`pre-room-layout${c?" has-recent-dubs":""}`,children:[t.jsxs("section",{className:"panel public-lobby-browser",children:[t.jsxs("div",{className:"public-lobby-header",children:[t.jsxs("div",{children:[t.jsx("span",{children:T("MULTIPLAYER")}),t.jsx("h2",{children:T("Multiplayer Lobby")})]}),t.jsx("button",{className:"icon-button",type:"button",onClick:O,"aria-label":T("Refresh rooms"),children:t.jsx(OC,{size:18,className:w?"spinning":""})})]}),t.jsxs("div",{className:"public-lobby-filters",children:[t.jsxs("label",{children:[t.jsx("span",{children:T("Language")}),t.jsxs("select",{className:"select-input",value:E,onChange:X=>oe(X.target.value),children:[t.jsx("option",{value:"all",children:T("All Languages")}),mh.map(X=>t.jsx("option",{value:X.value,children:X.label},X.value))]})]}),t.jsxs("label",{children:[t.jsx("span",{children:T("Status")}),t.jsxs("select",{className:"select-input",value:z,onChange:X=>W(X.target.value),children:[t.jsx("option",{value:"all",children:T("All Rooms")}),t.jsx("option",{value:"lobby",children:T("Waiting Only")})]})]})]}),M?t.jsx("p",{className:"public-lobby-error",children:M}):null,t.jsxs("div",{className:"public-room-list",children:[Y.map(X=>t.jsx(Z0,{room:X,joining:l==="join",onJoin:B},X.listingId)),!w&&!Y.length?t.jsx("p",{className:"public-room-empty",children:T("No rooms match these filters.")}):null,w&&!Y.length?t.jsx("p",{className:"public-room-empty",children:T("Loading rooms...")}):null]})]}),t.jsx("section",{className:"panel pre-room-panel",children:t.jsxs("div",{className:"panel-body control-stack",children:[r!=="Create or join a room."?t.jsx("p",{className:"pre-room-status",role:"status",children:T(r)}):null,t.jsxs("label",{className:"field-label pre-room-name-field",children:[T("Player Name"),t.jsx("input",{className:"select-input",value:e,onCh
43ange:X=>N(tS(X.target.value))})]}),t.jsxs("div",{className:"room-visibility-picker",role:"group","aria-label":T("Room visibility"),children:[t.jsx("button",{className:p==="private"?"active":"",type:"button",onClick:()=>L("private"),children:T("Private")}),t.jsx("button",{className:p==="public"?"active":"",type:"button",onClick:()=>L("public"),children:T("Public")})]}),p==="public"?t.jsxs("label",{className:"field-label pre-room-language-field",children:[T("Room Language"),t.jsx("select",{className:"select-input",value:f,onChange:X=>_(X.target.value),children:mh.map(X=>t.jsx("option",{value:X.value,children:X.label},X.value))})]}):null,p==="public"&&!b?t.jsx("button",{className:"ghost-button pre-room-sign-in-button",type:"button",onClick:K,children:T("Sign in to create a public room")}):null,t.jsxs("button",{className:"action-button pre-room-create-button",type:"button",disabled:!!l||p==="public"&&!b,onClick:D,title:T("Create Room"),children:[t.jsx(cw,{size:18}),T(l==="create"?"Creating...":"Create Room")]}),t.jsxs("label",{className:"field-label pre-room-code-field",children:[T("Room Code"),t.jsx("input",{className:"select-input room-code-input",maxLength:8,value:o,onChange:X=>j(X.target.value.toUpperCase())})]}),t.jsxs("button",{className:"ghost-button pre-room-join-button",type:"button",disabled:!!l||!o.trim(),onClick:I,title:T("Join Room"),children:[t.jsx(In,{size:18}),T(l==="join"?"Joining...":"Join Room")]}),t.jsx("div",{className:"pre-room-pack-controls",children:i})]})}),c]})}const mh=[{value:"en",label:"English"},{value:"de",label:"Deutsch"},{value:"ru",label:"Ð ÑÑÑкий"},{value:"uk",label:"УкÑаÑнÑÑка"},{value:"nl",label:"Nederlands"},{value:"fr",label:"Français"},{value:"it",label:"Italiano"},{value:"es",label:"Español"},{value:"pt",label:"Português"},{value:"other",label:"Other"}];function Z0({room:e,joining:o,onJoin:r}){const{t:i}=Be(),c=e.status==="lobby"?"Waiting":e.status==="playing"?"In Progress":"Results",l=e.visibility==="private",p=mh.find(f=>f.value===e.language)?.label??e.language;return t.jsxs("article",{className:`public-room-card ${l?"private":e.joinable?"joinable":"unavailable"}`,children:[t.jsx("div",{className:"public-room-host-avatar","aria-hidden":"true",children:l?t.jsx(wy,{size:18}):e.host?.avatarUrl?t.jsx("img",{src:e.host.avatarUrl,alt:""}):e.host?.name.slice(0,1).toUpperCase()}),t.jsxs("div",{className:"public-room-card-main",children:[t.jsx("strong",{children:e.packTitle||i("Waiting for host to choose a mod")}),t.jsxs("span",{children:[l?i("Private Room"):e.host?.name," · ",p]}),t.jsxs("small",{children:[i(c)," · ",e.onlinePlayerCount,"/",e.maxPlayers]})]}),t.jsx("button",{className:"ghost-button",type:"button",disabled:l||!e.joinable||o,onClick:()=>e.roomCode&&r(e.roomCode),children:l?t.jsxs(t.Fragment,{children:[t.jsx(wy,{size:15})," ",i("Private")]}):e.joinable?i("Join"):i(c)})]})}function X0({pack:e,isHost:o,packSelector:r,onLeaveRoom:i}){const{t:c}=Be();return t.jsxs("aside",{className:"room-side-panel",children:[t.jsx(Q0,{pack:e,isHost:o,packSelector:r}),t.jsxs("button",{className:"danger-button room-leave-button",type:"button",onClick:i,title:c("Leave Room"),children:[t.jsx(wc,{size:18}),c("Leave Room")]})]})}function Q0({pack:e,isHost:o,packSelector:r}){const i=Fi(e.roles),c=i.slice(0,3),l=Math.max(0,i.length-c.length),p=i.length?`Roles (${i.length}): ${i.join(", ")}`:void 0;return t.jsxs("section",{className:"panel selected-pack-card",children:[t.jsx("div",{className:"selected-pack-cover","aria-hidden":"true",children:e.coverUrl?t.jsx("img",{alt:"",src:e.coverUrl}):t.jsx(bt,{size:54})}),t.jsxs("div",{className:"selected-pack-content",children:[t.jsx("span",{children:e.typeLabel}),t.jsx("h2",{title:e.rawTitle||e.title,children:e.title}),e.creatorLabel?t.jsx("p",{className:"selected-pack-creator",children:e.creatorLabel}):null,e.linesLabel||e.videoLabel?t.jsxs("div",{className:"selected-pack-meta",children:[e.linesLabel?t.jsx("strong",{children:e.linesLabel}):null,e.videoLabel?t.jsx("strong",{children:e.videoLabel}):null]}):null,p?t.jsxs("div",{className:"selected-pack-roles",title:p,"aria-label":p,children:[t.jsxs("span",{children:["Roles ",t.jsx("small",{children:i.length})]}),t.jsx("strong",{children:c.join(" · ")}),l?t.jsxs("em",{children:["+",l]}):null]}):null,o&&r?t.jsx("div",{className:"button-row",children:r}):null]})]})}function eT({roomCode:e,players:o,currentPlayerId:r,maxPlayers:i,isHost:c,currentPlayerReady:l,startDisabledReason:p,startReadyMessage:f,startLabel:b,starting:y,installingRoomPack:w,roomPackInstallPercent:M,countdown:E,copiedTarget:z,chatPanel:N,recoveryMessage:j,onCopyCode:D,onCopyInviteLink:I,onReadyToggle:B,onStartGame:L,onKickPlayer:_,onReportPlayer:oe=()=>{},reportingEnabled:W=!1}){return t.jsxs("section",{className:"lobby-room-page",children:[t.jsxs("div",{className:"lobby-main-layout",children:[t.jsx(rT,{roomCode:e,copiedTarget:z,onCopyCode:D,onCopyInviteLink:I}),t.jsxs("section",{className:"panel player-lobby-panel",children:[j?t.jsxs("div",{className:"room-recovery-banner",role:"status",children:[t.jsx(bt,{size:17}),t.jsxs("span",{children:[t.jsx("strong",{children:"Recovery loaded"}),j]})]}):null,t.jsx(aT,{players:o,currentPlayerId:r,maxPlayers:i,currentUserIsHost:c,onKickPlayer:_,onReportPlayer:oe,reportingEnabled:W}),N,t.jsx("div",{className:"lobby-action-dock",children:c?t.jsx(dT,{disabled:!!p||y,reason:p,readyMessage:f,label:b,starting:y,countdown:E,onStartGame:L}):t.jsx(lT,{ready:l,disabled:y||w,installing:w,installPercent:M,onReadyToggle:B})})]})]}),E?t.jsx("div",{className:"lobby-countdown","aria-live":"assertive",children:E}):null]})}function tT(e,o){const r=Math.min(1512,Math.max(0,e-96)),i=e<=800?2:Math.max(3,Math.min(7,Math.floor((r+12)/202))),c=o<=720?2:o<=940?3:4;return i*c}function oT({packTitle:e,roles:o,claims:r,currentPlayerId:i,isHost:c,pendingRole:l,startDisabledReason:p,starting:f,chatPanel:b,roleLineCounts:y,onRoleChange:w,onStart:M,onBack:E}){const{t:z}=Be(),[N,j]=m.useState(0),[D,I]=m.useState(28),B=new Set(r.map(W=>W.role.toLocaleLowerCase())),L=o.filter(W=>!B.has(W.toLocaleLowerCase())).length,_=Math.max(1,Math.ceil(o.length/D)),oe=o.slice(N*D,(N+1)*D);return m.useEffect(()=>{const W=()=>{I(tT(window.innerWidth,window.innerHeight))};return W(),window.addEventListener("resize",W),()=>window.removeEventListener("resize",W)},[]),m.useEffect(()=>{j(W=>Math.min(W,_-1))},[_]),t.jsxs("section",{className:"role-selection-stage panel",children:[t.jsxs("header",{className:"role-selection-heading",children:[t.jsxs("div",{children:[t.jsx("h2",{children:z("Choose your roles")}),t.jsx("p",{children:e}),t.jsx("p",{className:"role-selection-help",children:z("Click a role to claim it. Click again to release it.")})]}),t.jsx("div",{className:"role-selection-heading-actions",children:c?t.jsx("button",{className:"ghost-button",type:"button",onClick:E,children:z("Back to room")}):null})]}
43),t.jsx("div",{className:"role-selection-progress",role:"status",children:t.jsxs("strong",{children:[o.length-L,"/",o.length]})}),t.jsxs("div",{className:"role-selection-layout",children:[t.jsx("div",{className:"role-selection-list",children:oe.map(W=>{const O=r.find(X=>X.role.toLocaleLowerCase()===W.toLocaleLowerCase()),K=O&&(O.playerId===i||c),T=O?.playerId===i,Y=y?.[W.toLocaleLowerCase()];return t.jsxs("button",{className:`role-selection-card${O?" claimed":""}${T?" mine":""}`,type:"button",disabled:!!l||!!(O&&!K),"aria-pressed":T,"aria-label":`${z(O?"Release":"Claim")} ${W}${O?` · ${O.playerName}`:""}`,onClick:()=>w(W,!!O),children:[t.jsx("strong",{children:W}),Y!==void 0?t.jsx("small",{children:z("{count} lines",{count:Y})}):null,O?t.jsx("span",{children:O.playerName}):null,T?t.jsx(Hd,{className:"role-selection-owned-check",size:19,"aria-hidden":"true"}):null]},W)})}),_>1?t.jsxs("nav",{className:"role-selection-pagination","aria-label":z("Choose your roles"),children:[t.jsx("button",{className:"ghost-button",type:"button",disabled:N===0,onClick:()=>j(W=>Math.max(0,W-1)),children:z("Back")}),t.jsxs("strong",{children:[N+1," / ",_]}),t.jsx("button",{className:"ghost-button",type:"button",disabled:N>=_-1,onClick:()=>j(W=>Math.min(_-1,W+1)),children:z("Next")})]}):null]}),t.jsxs("div",{className:"role-selection-bottom",children:[b?t.jsx("div",{className:"role-selection-chat-slot",children:b}):t.jsx("span",{}),t.jsx("footer",{className:"role-selection-footer",children:c?t.jsxs("button",{className:"action-button",type:"button",disabled:!!p||f,title:p?z(p):void 0,onClick:M,children:[t.jsx(_e,{size:18}),z(f?"Starting...":"Start Dubbing")]}):t.jsx("span",{children:z("Waiting for the host to start dubbing.")})})]})]})}function rT({roomCode:e,copiedTarget:o,onCopyCode:r,onCopyInviteLink:i}){const{t:c}=Be();return t.jsxs("section",{className:"panel room-invite-card",children:[t.jsxs("div",{children:[t.jsx("span",{children:c("ROOM CODE")}),t.jsx("strong",{className:"room-code-value",children:e})]}),t.jsxs("div",{className:"room-invite-actions",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:r,title:c("Copy Code"),children:[o==="code"?t.jsx(Hd,{size:18}):t.jsx(Ga,{size:18}),c(o==="code"?"Copied":"Copy Code")]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:i,title:c("Copy Invite Link"),children:[o==="link"?t.jsx(Hd,{size:18}):t.jsx(UC,{size:18}),c(o==="link"?"Copied":"Copy Invite Link")]})]})]})}function aT({players:e,currentPlayerId:o,maxPlayers:r,currentUserIsHost:i,onKickPlayer:c,onReportPlayer:l,reportingEnabled:p}){return t.jsx("div",{className:`lobby-player-grid slots-${r}`,children:Array.from({length:r},(f,b)=>{const y=e[b];return y?t.jsx(nT,{player:y,slotNumber:b+1,isCurrentPlayer:y.id===o,currentUserIsHost:i,onKickPlayer:c,onReportPlayer:l,reportingEnabled:p},y.id):t.jsx(cT,{},`empty-${b}`)})})}function nT({player:e,slotNumber:o,isCurrentPlayer:r,currentUserIsHost:i,onKickPlayer:c,onReportPlayer:l,reportingEnabled:p}){const{t:f}=Be(),b=!!e.reconnectDeadlineAt,y=b?"Reconnecting":e.isHost?"Host":e.isReady?"Ready":"Not Ready",w=b?"reconnecting":e.isHost?"host":e.isReady?"ready":"waiting",M=e.avatarUrl?t.jsx("img",{src:e.avatarUrl,alt:""}):e.accountUserId?e.name.slice(0,1).toUpperCase():String(o).padStart(2,"0"),E=e.supporterAvatarFrame?` ${eu(e.supporterAvatarFrame)}`:"",z=!r&&(p||i&&!e.isHost);return t.jsxs("article",{className:`lobby-player-card ${w}${r?" current-player":""}`,children:[e.accountUserId?t.jsx("a",{className:`player-avatar player-profile-avatar${E}`,href:`/workshop/creators/${encodeURIComponent(e.accountUserId)}`,target:"_blank",rel:"noopener","aria-label":`${f("View Profile")}: ${e.name}`,children:M}):t.jsx("div",{className:`player-avatar${E}`,"aria-hidden":"true",children:M}),t.jsx("div",{className:"player-card-main",children:t.jsxs("div",{className:"player-name-row",children:[t.jsx("strong",{children:t.jsx(_o,{name:e.name,identity:e.supporterIdentity,nameColor:e.supporterNameColor,showBadge:!1})}),t.jsx("span",{className:`player-ready-pill ${w}`,children:f(y)})]})}),e.supporterIdentity||z?t.jsxs("div",{className:"player-card-trailing",children:[e.supporterIdentity?t.jsx(tu,{identity:e.supporterIdentity}):null,z?t.jsx(iT,{currentUserIsHost:i,isCurrentPlayer:r,moderationUserId:e.moderationUserId,playerIsHost:e.isHost,playerName:e.name,reportingEnabled:p,onKickPlayer:c,onReportPlayer:l}):null]}):null]})}function iT({currentUserIsHost:e,isCurrentPlayer:o,moderationUserId:r,playerIsHost:i,playerName:c,reportingEnabled:l,onKickPlayer:p,onReportPlayer:f}){const{t:b}=Be(),[y,w]=m.useState(!1),[M,E]=m.useState(!1),z=m.useRef(null),N=m.useRef(null),j=m.useRef(null),[D,I]=m.useState({left:8,top:8});return m.useEffect(()=>{if(!y)return;function B(_){z.current&&!z.current.contains(_.target)&&!j.current?.contains(_.target)&&w(!1)}function L(_){_.key==="Escape"&&w(!1)}return document.addEventListener("mousedown",B),document.addEventListener("keydown",L),()=>{document.removeEventListener("mousedown",B),document.removeEventListener("keydown",L)}},[y]),m.useEffect(()=>{if(!y)return;function B(){const _=N.current;if(!_)return;const oe=_.getBoundingClientRect(),W=j.current?.getBoundingClientRect(),O=W?.width||190,K=W?.height||120,T=8,Y=6,X=Math.max(T,window.innerWidth-O-T),me=Math.min(X,Math.max(T,oe.right-O)),We=oe.bottom+Y,Ae=We+K<=window.innerHeight-T?We:Math.max(T,oe.top-K-Y);I({left:me,top:Ae})}const L=window.requestAnimationFrame(B);return window.addEventListener("resize",B),window.addEventListener("scroll",B,!0),()=>{window.cancelAnimationFrame(L),window.removeEventListener("resize",B),window.removeEventListener("scroll",B,!0)}}
43,[y]),t.jsxs("div",{className:"host-player-menu",ref:z,children:[t.jsx("button",{ref:N,className:"icon-button",type:"button","aria-label":b("Player actions for {name}",{name:c}),"aria-expanded":y,onClick:()=>w(B=>!B),children:t.jsx(lw,{size:19})}),y&&typeof document<"u"?th.createPortal(t.jsxs("div",{className:"host-player-menu-popover host-player-menu-popover-portal",ref:j,role:"menu",style:D,children:[!o&&l?t.jsxs("button",{type:"button",role:"menuitem",onClick:()=>{w(!1),E(!0)},children:[t.jsx(sw,{size:15}),b("Report Player")]}):null,e&&!i&&!o?t.jsxs("button",{className:"host-player-menu-danger",type:"button",role:"menuitem",onClick:()=>{w(!1),p(r,c)},children:[t.jsx(FC,{size:15}),b("Kick Player")]}):null]}),document.body):null,M&&typeof document<"u"?th.createPortal(t.jsx(sT,{playerName:c,onClose:()=>E(!1),onSubmit:(B,L)=>{E(!1),f(r,c,B,L)}}),document.body):null]})}function sT({playerName:e,onClose:o,onSubmit:r}){const{t:i}=Be(),[c,l]=m.useState(""),[p,f]=m.useState(""),b=p.trim(),y=!!c&&b.length>=10;return m.useEffect(()=>{function w(M){M.key==="Escape"&&o()}return document.addEventListener("keydown",w),()=>document.removeEventListener("keydown",w)},[o]),t.jsx("div",{className:"player-report-backdrop",onMouseDown:w=>{w.target===w.currentTarget&&o()},children:t.jsxs("section",{className:"player-report-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"player-report-title",children:[t.jsxs("header",{className:"player-report-header",children:[t.jsx("h2",{id:"player-report-title",children:i("Report Player")}),t.jsx("button",{className:"icon-button",type:"button",onClick:o,"aria-label":i("Close report dialog"),children:t.jsx(Tt,{size:19})})]}),t.jsxs("form",{className:"player-report-form",onSubmit:w=>{w.preventDefault(),c&&y&&r(c,b)},children:[t.jsxs("div",{className:"player-report-target",children:[t.jsx("span",{children:i("Reported Player")}),t.jsx("strong",{children:e})]}),t.jsxs("label",{children:[t.jsx("span",{children:i("Report Type")}),t.jsxs("select",{autoFocus:!0,value:c,onChange:w=>l(w.target.value),required:!0,children:[t.jsx("option",{value:"",children:i("Choose a violation type")}),q0.map(w=>t.jsx("option",{value:w,children:i(w)},w))]})]}),t.jsxs("label",{children:[t.jsx("span",{children:i("Description")}),t.jsx("textarea",{value:p,onChange:w=>f(w.target.value.slice(0,500)),rows:6,minLength:10,maxLength:500,placeholder:i("Describe what happened so moderators can review the report."),required:!0}),t.jsxs("small",{children:[i("At least 10 characters.")," ",p.length,"/500"]})]}),t.jsxs("div",{className:"player-report-actions",children:[t.jsx("button",{className:"ghost-button",type:"button",onClick:o,children:i("Cancel")}),t.jsx("button",{className:"action-button",type:"submit",disabled:!y,children:i("Submit Report")})]})]})]})})}function cT(){const{t:e}=Be();return t.jsxs("article",{className:"lobby-player-card empty-slot","aria-label":e("Empty player slot"),children:[t.jsx("div",{className:"player-avatar empty","aria-hidden":"true",children:t.jsx(cw,{size:18})}),t.jsx("div",{className:"empty-slot-copy","aria-hidden":"true",children:t.jsx("strong",{children:e("Empty player slot")})})]})}function lT({ready:e,disabled:o,installing:r,installPercent:i,onReadyToggle:c}){const{t:l}=Be(),p=typeof i=="number"?Math.max(0,Math.min(100,Math.round(i))):void 0,f={"--install-progress":`${p??0}%`};return t.jsx("button",{className:`${e?"ghost-button":"action-button"} ready-toggle-button ${r?"installing-button":""}`,type:"button",disabled:o,onClick:c,children:r?t.jsxs("span",{className:`install-progress-label ${p===void 0?"indeterminate":""}`,style:f,children:[t.jsx("span",{className:"install-progress-ring","aria-hidden":"true"}),t.jsx("span",{children:p===void 0?l("Installing room mod"):l("Installing room mod {percent}%",{percent:p})})]}):t.jsxs(t.Fragment,{children:[e?t.jsx(Hd,{size:18}):t.jsx(_e,{size:18}),l(e?"Cancel Ready":"Ready Up")]})})}function dT({disabled:e,reason:o,readyMessage:r,label:i,starting:c,countdown:l,onStartGame:p}){const{t:f}=Be(),b=o||r;return t.jsx("div",{className:"start-game-stack",children:t.jsxs("button",{className:"action-button start-game-button",type:"button",disabled:e,onClick:p,title:f(b),children:[t.jsx(_e,{size:18}),c?f("Starting..."):l?f("Starting in {count}",{count:l}):f(i??"Start Game")]})})}function nS({messages:e,players:o=[],connected:r,error:i,notice:c,hostStartDeadlineAt:l,onSend:p,focusCompose:f=!1,onEmptySubmit:b}){const{t:y}=Be(),[w,M]=m.useState(""),E=mT(l),z=m.useRef(null),N=m.useRef(null),j=e.filter(I=>!iS(I));m.useEffect(()=>{z.current?.scrollTo({top:z.current.scrollHeight})},[e,E,c]),m.useEffect(()=>{f&&N.current?.focus()},[f]);const D=()=>{const I=w.trim();if(!I){b?.();return}r&&(p(I),M(""))};return t.jsxs("aside",{className:"room-chat-panel open fixed-chat",children:[t.jsxs("div",{className:"room-chat-label","aria-label":y("Room Chat"),children:[t.jsx(qd,{size:18}),t.jsx("span",{children:y("Room Chat")})]}),t.jsxs("div",{className:"room-chat-body",children:[t.jsxs("div",{className:"room-chat-messages",ref:z,"aria-live":"polite",children:[c?t.jsxs("div",{className:"room-chat-message system room-chat-notice",role:"status",children:[t.jsx(sw,{size:13}),t.jsx("span",{children:y(c)})]}):null,j.map(I=>{const B=o.find(L=>L.id===I.playerId);return t.jsxs("div",{className:`room-chat-message ${I.kind}`,children:[I.kind==="user"?t.jsx("strong",{children:t.jsx(_o,{name:I.playerName??"Player",identity:B?.supporterIdentity,nameColor:B?.supporterNameColor})}):null,t.jsx("span",{children:I.kind==="system"?y(I.text):I.text}),t.jsx("time",{children:new Date(I.createdAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})})]},I.id)}),E>0?t.jsx("div",{className:"room-chat-message system countdown",role:"status",children:t.jsx("span",{children:y("Host must start: {count}s",{count:E})})}):null,!c&&!j.length&&E<=0?t.jsx("p",{children:y("No messages yet.")}):null]}),i?t.jsx("p",{className:"room-chat-error",children:i}):null,t.jsxs("div",{className:"room-chat-compose",children:[t.jsx("input",{ref:N,maxLength:300,placeholder:y(r?"Message the room":"Reconnecting chat..."),value:w,disabled:!r,onChange:I=>M(I.target.value),onKeyDown:I=>
43{I.key==="Enter"&&!I.shiftKey&&(I.preventDefault(),D())}}),t.jsx("button",{className:"icon-button",type:"button",disabled:!r||!w.trim(),onClick:D,"aria-label":y("Send message"),children:t.jsx(iw,{size:18})})]})]})]})}function uT({messages:e,players:o=[],connected:r,error:i,onSend:c}){const{t:l}=Be(),[p,f]=m.useState(!1),[b,y]=m.useState(""),[w,M]=m.useState(e.length>0),E=m.useRef(null),z=m.useRef(void 0),N=e.filter(I=>!iS(I)).slice(-6),j=N.at(-1)?.id;m.useEffect(()=>{const I=B=>{if(B.key==="Escape"&&p){B.preventDefault(),f(!1);return}if(B.key!=="Enter"||p||B.repeat)return;const L=B.target;L instanceof HTMLInputElement||L instanceof HTMLTextAreaElement||L instanceof HTMLSelectElement||L instanceof HTMLElement&&L.isContentEditable||(B.preventDefault(),M(!0),f(!0))};return window.addEventListener("keydown",I,!0),()=>window.removeEventListener("keydown",I,!0)},[p]),m.useEffect(()=>{p&&E.current?.focus()},[p]),m.useEffect(()=>(window.clearTimeout(z.current),j&&M(!0),j&&!p&&(z.current=window.setTimeout(()=>M(!1),8e3)),()=>window.clearTimeout(z.current)),[j,p]);const D=()=>{const I=b.trim();I&&r&&c(I),I&&r&&y(""),f(!1)};return t.jsxs("aside",{className:`role-selection-chat${p?" composing":""}`,"aria-label":l("Room Chat"),children:[w&&N.length?t.jsx("div",{className:"role-selection-chat-messages","aria-live":"polite",children:N.map(I=>{const B=o.find(L=>L.id===I.playerId);return t.jsxs("div",{className:`role-selection-chat-message ${I.kind}`,children:[I.kind==="user"?t.jsx("strong",{children:t.jsx(_o,{name:I.playerName??"Player",identity:B?.supporterIdentity,nameColor:B?.supporterNameColor})}):null,t.jsx("span",{children:I.kind==="system"?l(I.text):I.text})]},I.id)})}):null,i?t.jsx("p",{className:"role-selection-chat-error",children:i}):null,p?t.jsxs("div",{className:"role-selection-chat-compose",children:[t.jsx(qd,{size:18,"aria-hidden":"true"}),t.jsx("input",{ref:E,maxLength:300,placeholder:l(r?"Message the room":"Reconnecting chat..."),value:b,disabled:!r,onChange:I=>y(I.target.value),onKeyDown:I=>{I.key==="Enter"&&!I.shiftKey&&(I.preventDefault(),D())}}),t.jsx("button",{className:"icon-button",type:"button",disabled:!r||!b.trim(),onClick:D,"aria-label":l("Send message"),children:t.jsx(iw,{size:18})}),t.jsx("button",{className:"icon-button role-selection-chat-cancel",type:"button",onClick:()=>f(!1),"aria-label":l("Close room chat"),children:t.jsx(Tt,{size:17})})]}):t.jsxs("button",{className:"role-selection-chat-trigger",type:"button",onClick:()=>{M(!0),f(!0)},"aria-label":l("Open room chat"),children:[t.jsx(qd,{size:18}),t.jsx("span",{children:l("Room Chat")}),t.jsx("kbd",{children:"Enter"})]})]})}function pT({messages:e,players:o,connected:r,error:i,onlineCount:c,onSend:l,leadingControl:p,initiallyOpen:f=!1}){const{t:b}=Be(),[y,w]=m.useState(f),[M,E]=m.useState(0),z=m.useRef(e.at(-1)?.id);return m.useEffect(()=>{const N=e.at(-1)?.id;N&&N!==z.current&&!y&&E(j=>Math.min(99,j+1)),z.current=N,y&&E(0)},[e,y]),m.useEffect(()=>{if(!y)return;const N=j=>{j.key==="Escape"&&w(!1)};return window.addEventListener("keydown",N),()=>window.removeEventListener("keydown",N)},[y]),m.useEffect(()=>{const N=j=>{if(j.key!=="Enter"||j.repeat||j.shiftKey)return;const D=j.target;D instanceof HTMLInputElement||D instanceof HTMLTextAreaElement||D instanceof HTMLSelectElement||D instanceof HTMLButtonElement||D instanceof HTMLElement&&D.isContentEditable||(j.preventDefault(),w(I=>!I),E(0))};return window.addEventListener("keydown",N,!0),()=>window.removeEventListener("keydown",N,!0)},[]),t.jsxs("div",{className:`in-game-room-chat${y?" open":""}`,children:[y?t.jsxs("div",{className:"in-game-room-chat-drawer",children:[t.jsx("button",{className:"in-game-room-chat-close",type:"button",onClick:()=>w(!1),"aria-label":b("Close room chat"),children:t.jsx(Tt,{size:17})}),t.jsx(nS,{messages:e,players:o,connected:r,error:i,onSend:l,focusCompose:!0,onEmptySubmit:()=>w(!1)})]}):null,t.jsxs("div",{className:"in-game-room-chat-actions",children:[p,t.jsxs("button",{className:"in-game-room-chat-trigger",type:"button",onClick:()=>{w(N=>!N),E(0)},"aria-label":b(y?"Close room chat":"Open room chat"),"aria-expanded":y,children:[t.jsx(qd,{size:19}),t.jsx("span",{children:b("Room Chat")}),t.jsxs("b",{className:"in-game-room-online",children:[t.jsx(In,{size:14})," ",c,"/",o.length]}),M?t.jsx("i",{"aria-label":b("{count} unread messages",{count:M}),children:M}):null]})]})]})}function mT(e){const[o,r]=m.useState(()=>pv(e));return m.useEffect(()=>{const i=()=>r(pv(e));if(i(),!e)return;const c=window.setInterval(i,250);return()=>window.clearInterval(c)},[e]),o}function pv(e){const o=e?Date.parse(e):Number.NaN;return Number.isFinite(o)?Math.max(0,Math.ceil((o-Date.now())/1e3)):0}function iS(e){return e.kind==="system"&&e.text==="Everyone is ready. The host has 10 seconds to start the game."}function hT({message:e}){const{t:o}=Be();return t.jsx("div",{className:`lobby-toast ${e?"show":""}`,role:"status","aria-live":"polite",children:e&&o(e)})}const Et=[{key:"duration",name:"Dura",title:"Duration"},{key:"timing",name:"Tempo",title:"Timing"},{key:"energy",name:"Volt",title:"Energy"},{key:"pitch",name:"Echo",title:"Pitch"}],Cc={duration:"/judge-stage-judge1.webp",timing:"/judge-stage-judge2.webp",energy:"/judge-stage-judge3.webp",pitch:"/judge-stage-judge4.webp"},gT=["duration","timing","energy","pitch"],fT={duration:"Duration",timing:"Timing",energy:"Energy",pitch:"Pitch"},mv={duration:[{min:85,verdict:"Perfect Fit",shortVerdict:"Perfect",tone:"elite"},{min:65,verdict:"Good Fit",shortVerdict:"Good",tone:"high"},{min:40,verdict:"Uneven",shortVerdict:"Uneven",tone:"mid"}
43,{min:0,verdict:"Too Short/Long",shortVerdict:"Off",tone:"low"}],timing:[{min:85,verdict:"Locked In",shortVerdict:"Locked",tone:"elite"},{min:65,verdict:"On Time",shortVerdict:"On Time",tone:"high"},{min:40,verdict:"Close",shortVerdict:"Close",tone:"mid"},{min:0,verdict:"Off Beat",shortVerdict:"Off",tone:"low"}],energy:[{min:85,verdict:"Stage Ready",shortVerdict:"Stage",tone:"elite"},{min:65,verdict:"Expressive",shortVerdict:"Expressive",tone:"high"},{min:40,verdict:"Solid",shortVerdict:"Solid",tone:"mid"},{min:0,verdict:"Flat",shortVerdict:"Flat",tone:"low"}],pitch:[{min:85,verdict:"Uncanny",shortVerdict:"Uncanny",tone:"elite"},{min:65,verdict:"Very Close",shortVerdict:"Close",tone:"high"},{min:40,verdict:"Close Enough",shortVerdict:"Close",tone:"mid"},{min:0,verdict:"Different",shortVerdict:"Different",tone:"low"}]};function bT(e){return gT.map(o=>hh(o,e[o]))}function hh(e,o){const r=Number.isFinite(o)?Math.max(0,Math.min(100,o)):0,i=mv[e].find(c=>r>=c.min)??mv[e][0];return{key:e,label:fT[e],verdict:i.verdict,shortVerdict:i.shortVerdict,tone:i.tone}}const yT={cover:"/menu-cover.png",menuBackground:"/menu-cover.png",logo:"/voxalike-logo.png",judgeBackground:"/judge-stage-background.webp"};function vT(e){return yT[e]}function hv(e){return`url(${JSON.stringify(e)})`}function kT(e){return["cover","menuBackground","logo","stageFrame","judgeBackground","resultFrame"].find(r=>!!e[r])}const Jo={maxZipBytes:50*1024*1024,maxExpandedBytes:100*1024*1024,maxFiles:40,maxFileBytes:30*1024*1024,mediaDecodeTimeoutMs:1e4},ou=["cover","menuBackground","logo","stageFrame","judgeBackground","resultFrame"],wT=/^[a-z0-9][a-z0-9._-]{0,63}$/,ST=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,xT=new Set(["png","jpg","jpeg","webp"]);function RT(e){if(!bv(e))throw new Error("theme.json must contain a JSON object.");if(e.schemaVersion!==1)throw new Error("Unsupported theme schemaVersion. Visual Theme MVP requires schemaVersion 1.");const o=gv(e.id,"Theme id",64).toLowerCase();if(!wT.test(o))throw new Error("Theme id must use lowercase letters, numbers, dots, underscores, or hyphens.");const r=gv(e.title,"Theme title",80),i=fv(e.author,80),c=fv(e.version,32),l=e.assets;if(l!==void 0&&!bv(l))throw new Error("Theme assets must be a JSON object.");const p={};if(l){const f=new Set(ou),b=Object.keys(l).find(y=>!f.has(y));if(b)throw new Error(`Unknown theme asset slot: ${b}.`);for(const y of ou){const w=l[y];if(w==null||w==="")continue;if(typeof w!="string")throw new Error(`Theme asset ${y} must be a relative file path.`);const M=sS(w),E=lS(M);if(!xT.has(E))throw new Error(`${y} must use PNG, JPG, JPEG, or WebP.`);p[y]=M}}return{schemaVersion:1,id:o,title:r,author:i,version:c,assets:p}}function sS(e){const o=e.trim();if(!o||o.includes("\0")||ST.test(o)||o.startsWith("/")||o.startsWith("\\")||/^[a-z]:/i.test(o)||o.includes("\\"))throw new Error(`Unsafe theme asset path: ${e}.`);const r=o.split("/");if(r.some(i=>!i||i==="."||i===".."))throw new Error(`Unsafe theme asset path: ${e}.`);return r.join("/")}function ru(e){return lS(e)}function gv(e,o,r){if(typeof e!="string")throw new Error(`${o} is required.`);const i=cS(e,r);if(!i)throw new Error(`${o} is required.`);return i}function fv(e,o){return typeof e=="string"&&cS(e,o)||void 0}function cS(e,o){return e.replace(/\s+/g," ").trim().slice(0,o)}function lS(e){const o=e.split("/").at(-1)??"",r=o.lastIndexOf(".");return r>=0?o.slice(r+1).toLowerCase():""}function bv(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}const bu={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",webp:"image/webp"},PT=new Set(["json",...Object.keys(bu)]);async function CT(e){if(!e.name.toLowerCase().endsWith(".zip"))throw new Error("Visual themes must be imported as ZIP files.");if(!e.size)throw new Error("The theme ZIP is empty.");if(e.size>Jo.maxZipBytes)throw new Error(`Theme ZIP is too large. Maximum size is ${Cm(Jo.maxZipBytes)} MB.`);const{default:o}=await Ko(async()=>{const{default:E}=await import("./jszip.min-CK9NPRCS.js").then(z=>z.j);return{default:E}},__vite__mapDeps([5,2,6])),r=await e.arrayBuffer(),i=await o.loadAsync(r,{checkCRC32:!0}),c=Object.values(i.files).filter(E=>!E.dir);if(!c.length)throw new Error("The theme ZIP does not contain any files.");if(c.length>Jo.maxFiles)throw new Error(`Theme ZIP contains too many files. Maximum count is ${Jo.maxFiles}.`);const l=new Map;let p=0;for(const E of c){const z=E.unsafeOriginalName,N=sS(z??E.name),j=ru(N);if(!PT.has(j))throw new Error(`Theme ZIP contains a prohibited file type: ${N}.`);if(j==="json"&&N.toLowerCase()!=="theme.json")throw new Error(`Only the root theme.json manifest is allowed: ${N}.`);const D=await E.async("uint8array");if(D.byteLength>Jo.maxFileBytes)throw new Error(`${N} is too large. Maximum file size is ${Cm(Jo.maxFileBytes)} MB.`);if(p+=D.byteLength,p>Jo.maxExpandedBytes)throw new Error(`Theme ZIP expands beyond ${Cm(Jo.maxExpandedBytes)} MB.`);if(l.has(N.toLowerCase()))throw new Error(`Theme ZIP contains a duplicate path: ${N}.`);l.set(N.toLowerCase(),D)}const f=l.get("theme.json");if(!f)throw new Error("theme.json must be present at the root of the ZIP.");let b;try{b=JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(f))}
43catch{throw new Error("theme.json is not valid UTF-8 JSON.")}const y=RT(b),w={},M=[];for(const E of ou){const z=y.assets[E];if(!z)continue;const N=l.get(z.toLowerCase());if(!N){M.push(`${E} is missing and will use the Voxalike default.`);continue}const j=ru(z),D=bu[j];if(!D||!dS(N,j)){M.push(`${E} has an invalid file header and will use the Voxalike default.`);continue}const I=N.slice().buffer,B=new Blob([I],{type:D});try{await pS(B,D,z)}catch{M.push(`${E} could not be decoded and will use the Voxalike default.`);continue}w[E]={path:z,mimeType:D,blob:B}}return{id:y.id,title:y.title,author:y.author,version:y.version,schemaVersion:1,packageHash:await jT(r),installedAt:Date.now(),assets:w,warnings:M}}function dS(e,o){return o==="png"?vv(e,[137,80,78,71,13,10,26,10]):o==="jpg"||o==="jpeg"?vv(e,[255,216,255]):o==="webp"?kv(e,0,"RIFF")&&kv(e,8,"WEBP"):!1}function uS(e){return!e||e.schemaVersion!==1||typeof e.id!="string"||typeof e.title!="string"||!/^[a-f0-9]{64}$/.test(e.packageHash)||!e.assets||typeof e.assets!="object"?!1:Object.entries(e.assets).every(([o,r])=>{if(!ou.includes(o)||!r||!(r.blob instanceof Blob))return!1;const i=ru(r.path),c=bu[i];return!!c&&r.mimeType===c&&r.blob.type===c&&r.blob.size>0&&r.blob.size<=Jo.maxFileBytes})}async function yv(e){if(!uS(e))return!1;try{for(const[o,r]of Object.entries(e.assets)){if(!r||!(r.blob instanceof Blob)||!r.blob.size||r.blob.size>Jo.maxFileBytes)return!1;const i=ru(r.path),c=bu[i];if(!c||r.mimeType!==c||r.blob.type!==c)return!1;const l=new Uint8Array(await r.blob.slice(0,16).arrayBuffer());if(!dS(l,i))return!1;await pS(r.blob,c,`${o}:${r.path}`)}return!0}catch{return!1}}async function pS(e,o,r){if(o.startsWith("image/")){await MT(e,r);return}throw new Error(`Theme media type is not supported: ${r}.`)}async function MT(e,o){if(typeof createImageBitmap=="function")try{const i=await createImageBitmap(e),c=i.width>0&&i.height>0;if(i.close(),!c)throw new Error("empty image");return}catch{throw new Error(`Theme image could not be decoded: ${o}.`)}if(typeof Image>"u")return;const r=URL.createObjectURL(e);try{await new Promise((i,c)=>{const l=new Image,p=globalThis.setTimeout(()=>c(new Error("timeout")),Jo.mediaDecodeTimeoutMs);l.onload=()=>{globalThis.clearTimeout(p),l.naturalWidth>0&&l.naturalHeight>0?i():c(new Error("empty image"))},l.onerror=()=>{globalThis.clearTimeout(p),c(new Error("decode failed"))},l.src=r})}catch{throw new Error(`Theme image could not be decoded: ${o}.`)}finally{URL.revokeObjectURL(r)}}async function jT(e){if(!globalThis.crypto?.subtle)throw new Error("This browser cannot verify theme package hashes.");const o=await globalThis.crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(o),r=>r.toString(16).padStart(2,"0")).join("")}function vv(e,o){return o.every((r,i)=>e[i]===r)}function kv(e,o,r){return[...r].every((i,c)=>e[o+c]===i.charCodeAt(0))}function Cm(e){return Math.round(e/1024/1024)}const TT="voxalike-visual-themes",ET=1,_a="themes",Ya="preferences",au="current",AT={activeThemeId:void 0};async function IT(){if(!$h())return[];const e=await Dc();try{const o=e.transaction(_a,"readonly"),r=await yu(o.objectStore(_a).getAll());return await Lc(o),r.sort((i,c)=>c.installedAt-i.installedAt)}finally{e.close()}}async function NT(e){const o=await Dc();try{const r=o.transaction(_a,"readwrite"),i=r.objectStore(_a);if(await yu(i.get(e.id)))throw r.abort(),new Error(`A theme with id "${e.id}" is already installed. Delete it before importing another package with the same id.`);i.add(e),await Lc(r)}finally{o.close()}}async function DT(e){const o=await Dc();try{const r=o.transaction([_a,Ya],"readwrite");r.objectStore(_a).delete(e);const i=r.objectStore(Ya),c=await yu(i.get(au));c?.activeThemeId===e&&i.put({...c,key:au,activeThemeId:void 0}),await Lc(r)}finally{o.close()}}async function LT(){if(!$h())return AT;const e=await Dc();try{const o=e.transaction(Ya,"readonly"),r=await yu(o.objectStore(Ya).get(au));return await Lc(o),{activeThemeId:typeof r?.activeThemeId=="string"?r.activeThemeId:void 0}}finally{e.close()}}async function wv(e){const o=await Dc();try{const r=o.transaction(Ya,"readwrite");r.objectStore(Ya).put({key:au,...e}),await Lc(r)}finally{o.close()}}function Sv(e){return{id:e.id,title:e.title,author:e.author,version:e.version,schemaVersion:e.schemaVersion,packageHash:e.packageHash,installedAt:e.installedAt,assetKeys:Object.keys(e.assets),warnings:e.warnings}}function $h(){return typeof indexedDB<"u"}function Dc(){return $h()?new Promise((e,o)=>{const r=indexedDB.open(TT,ET);r.onupgradeneeded=()=>{const i=r.result;i.objectStoreNames.contains(_a)||i.createObjectStore(_a,{keyPath:"id"}),i.objectStoreNames.contains(Ya)||i.createObjectStore(Ya,{keyPath:"key"})},r.onsuccess=()=>e(r.result),r.onerror=()=>o(r.error??new Error("Theme storage failed.")),r.onblocked=()=>o(new Error("Theme storage is blocked by another tab."))}):Promise.reject(new Error("Browser storage is unavailable."))}function yu(e){return new Promise((o,r)=>{e.onsuccess=()=>o(e.result),e.onerror=()=>r(e.error??new Error("Theme storage failed."))})}function Lc(e){return new Promise((o,r)=>{e.oncomplete=()=>o(),e.onerror=()=>r(e.error??new Error("Theme storage failed.")),e.onabort=()=>r(e.error??new Error("Theme storage was aborted."))})}
43const mS=m.createContext(void 0),Mm={};function VT({children:e}){const[o,r]=m.useState([]),i=m.useRef([]),[c,l]=m.useState(Mm),[p,f]=m.useState(!1),b=m.useRef(new Map),y=m.useRef(new Map),[w,M]=m.useState({}),[E,z]=m.useState({}),N=m.useCallback(()=>{b.current.forEach(O=>URL.revokeObjectURL(O)),b.current.clear()},[]),j=m.useCallback(()=>{y.current.forEach(O=>URL.revokeObjectURL(O)),y.current.clear()},[]);m.useEffect(()=>{let O=!1;return Promise.all([IT().catch(()=>[]),LT().catch(()=>Mm)]).then(async([K,T])=>{if(O)return;const Y=K.filter(uS),X=Y.find(Ae=>Ae.id===T.activeThemeId),me=X&&await yv(X)?X.id:void 0;if(O)return;const We={...T,activeThemeId:me};i.current=Y,r(Y),l(We),f(!0),T.activeThemeId&&!me&&wv(We).catch(()=>{})}),()=>{O=!0}},[]),m.useEffect(()=>{N();const O=o.find(T=>T.id===c.activeThemeId);if(!O){M({});return}const K={};for(const[T,Y]of Object.entries(O.assets)){if(!Y?.blob)continue;const X=URL.createObjectURL(Y.blob);b.current.set(T,X),K[T]=X}return M(K),N},[c.activeThemeId,N,o]),m.useEffect(()=>{j();const O={};for(const K of o){const T=kT(K.assets),Y=T?K.assets[T]:void 0;if(!Y?.blob)continue;const X=URL.createObjectURL(Y.blob);y.current.set(K.id,X),O[K.id]=X}return z(O),j},[j,o]),m.useEffect(()=>{const O=document.documentElement,K=w.menuBackground,T=w.resultFrame;return K?O.style.setProperty("--theme-menu-background-image",hv(K)):O.style.removeProperty("--theme-menu-background-image"),T?(O.style.setProperty("--theme-result-frame-image",hv(T)),O.dataset.visualThemeResultFrame="true"):(O.style.removeProperty("--theme-result-frame-image"),delete O.dataset.visualThemeResultFrame),()=>{O.style.removeProperty("--theme-menu-background-image"),O.style.removeProperty("--theme-result-frame-image"),delete O.dataset.visualThemeResultFrame}},[w.menuBackground,w.resultFrame]),m.useEffect(()=>()=>{N(),j()},[N,j]);const D=m.useCallback(async O=>{l(O);try{await wv(O)}catch{throw l(Mm),new Error("Theme preferences could not be saved. Default theme restored.")}},[]),I=m.useCallback(async O=>{const K=await CT(O);if(i.current.some(Y=>Y.id===K.id))throw new Error(`A theme with id "${K.id}" is already installed. Delete it before importing this package.`);await NT(K);const T=[K,...i.current];return i.current=T,r(T),Sv(K)},[]),B=m.useCallback(async O=>{const K=i.current.find(T=>T.id===O);if(!K||!await yv(K))throw await D({...c,activeThemeId:void 0}),new Error("That theme is missing or damaged. Default theme restored.");await D({...c,activeThemeId:O})},[D,c,o]),L=m.useCallback(async()=>{await D({...c,activeThemeId:void 0})},[D,c]),_=m.useCallback(async O=>{await DT(O);const K=i.current.filter(T=>T.id!==O);i.current=K,r(K),c.activeThemeId===O&&l(T=>({...T,activeThemeId:void 0}))},[c.activeThemeId]),oe=m.useCallback(O=>w[O]??vT(O),[w]),W=m.useMemo(()=>{const O=o.map(Sv);return{ready:p,themes:O,activeThemeId:c.activeThemeId,activeTheme:O.find(K=>K.id===c.activeThemeId),getThemeAsset:oe,getThemePreviewAsset:K=>E[K],importTheme:I,enableTheme:B,useDefaultTheme:L,deleteTheme:_}},[_,B,oe,I,c.activeThemeId,E,p,o,L]);return t.jsx(mS.Provider,{value:W,children:e})}function Gh(){const e=m.useContext(mS);if(!e)throw new Error("useTheme must be used inside ThemeProvider.");return e}const nc=750,zT=0,xv=["18%","35.5%","64.5%","82%"],OT=.42,Rv={duration:"/judge-sound-daodun.mp3",timing:"/judge-sound-xiaojiaomao.mp3",pitch:"/judge-sound-monster-growl.mp3"},hS=["/judge-stage-background.webp","/judge-speech-bubble.webp",...Object.values(Cc)];let jm;function Tm({score:e,judgePack:o,prepare:r,onStart:i,onComplete:c,staticPreview:l=!1}){const{getThemeAsset:p}=Gh(),{t:f}=Be(),b=p("judgeBackground"),[y,w]=m.useState(l),[M,E]=m.useState(l?Et.length*nc:0),z=Et.length*nc+zT,N=M>=z,j=m.useRef(r),D=m.useRef(i),I=m.useRef(new Set),B=m.useRef(new Set);m.useEffect(()=>{j.current=r,D.current=i},[r,i]),m.useEffect(()=>{I.current.clear()},[o,e.total]),m.useEffect(()=>()=>{B.current.forEach(W=>{W.pause(),W.removeAttribute("src")}),B.current.clear()},[]),m.useEffect(()=>{if(l){w(!0),E(z);return}let W=!1,O;E(0),w(!1);const K=j.current??UT,T=Object.values(o?.images??{});return Promise.all([K(),b?gh(b):Promise.resolve(),...Object.values(Rv).map(FT),...T.map(gh)]).catch(()=>{}).then(()=>{W||(w(!0),window.requestAnimationFrame(()=>{window.requestAnimationFrame(()=>{if(W)return;D.current?.();const Y=performance.now();O=window.setInterval(()=>{E(Math.min(z,performance.now()-Y))},48)})}))}),()=>{W=!0,O&&window.clearInterval(O)}},[b,o,e.total,l,z]),m.useEffect(()=>{if(!y||N)return;const W=Math.max(0,Math.min(Et.length-1,Math.floor(M/nc))),O=Et[W];if(!O||I.current.has(O.key))return;I.current.add(O.key);const T=hh(O.key,e[O.key]).tone;if(!o&&T!=="elite")return;const Y=o?.sounds?.[O.key]?.[T]??o?.sounds?.[O.key]?.reveal??Rv[O.key];if(!Y)return;const X=new Audio(Y);X.volume=OT,X.preload="auto",X.onended=()=>{B.current.delete(X)},X.onerror=()=>{B.current.delete(X)},B.current.add(X),X.play().catch(()=>{B.current.delete(X)})},[N,M,o,y,e]);const L=()=>{N&&c()},_=W=>{!N||W.key!=="Enter"&&W.key!==" "||(W.preventDefault(),c())},oe=m.useMemo(()=>Et.map((W,O)=>
43{const K=e[W.key],T=hh(W.key,K),Y=o?.feedback?.[W.key]?.[T.tone],X=Y??f(T.verdict),me=O*nc,We=Math.max(0,Math.min(1,(M-me)/nc)),Ae=We>=1||M>=z;return{...W,feedback:{...T,label:o?.labels?.[W.key]??f(T.label),verdict:X,shortVerdict:Y??f(T.shortVerdict)},customFeedback:!!Y,verdictLength:X.length,active:We>0&&!Ae,revealed:Ae,displayName:o?.names?.[W.key]??W.name,imageSrc:o?.images[W.key]??Cc[W.key]}}),[M,o,e,f,z]);return t.jsx("section",{className:`judge-show-layout ${N?"complete":""}`,role:N?"button":void 0,tabIndex:N?0:void 0,onClick:L,onKeyDown:_,"aria-busy":!y,children:t.jsxs("div",{className:`judge-stage-scene ${y?"":"judge-stage-loading"}`,"aria-label":f("Judge score stage"),children:[y?null:t.jsx("div",{className:"judge-loading-copy",children:f("Preparing score")}),t.jsx("img",{className:"judge-stage-background",src:b,alt:""}),oe.map((W,O)=>t.jsx("div",{className:`judge-stage-person judge-${W.key} ${W.active?"active":""} ${W.revealed?"revealed":""}`,style:{"--judge-x":xv[O]},children:t.jsx("img",{alt:"",src:W.imageSrc,title:W.displayName})},W.key)),oe.map((W,O)=>t.jsxs("div",{className:`score-machine textual judge-${W.key} ${W.feedback.tone} ${W.customFeedback?"custom-feedback":""} ${W.verdictLength>20?"long-feedback":W.verdictLength>12?"medium-feedback":""} ${W.active?"active":""} ${W.revealed?"revealed":""}`,style:{"--judge-x":xv[O]},"aria-label":`${W.feedback.label} feedback ${W.feedback.verdict}`,"aria-hidden":!W.active&&!W.revealed,children:[t.jsx("span",{children:W.customFeedback?W.displayName:f("Your {label} is",{label:W.feedback.label})}),t.jsx("strong",{children:W.feedback.verdict})]},`${W.key}-score`)),t.jsx("div",{className:"judge-stage-total","aria-label":f("Final score {score}",{score:N?e.total:f("pending")}),children:t.jsx("strong",{children:N?e.total:"--"})}),N?t.jsx("div",{className:"judge-continue-hint",children:f("Continue")}):null]})})}function UT(){return jm||(jm=Promise.allSettled(hS.map(gh)).then(()=>{})),jm}function gh(e){return new Promise(o=>{const r=new Image;r.decoding="async",r.onload=()=>{if(r.decode){r.decode().then(()=>o()).catch(()=>o());return}o()},r.onerror=()=>o(),r.src=e,r.complete&&o()})}function FT(e){return new Promise(o=>{const r=new Audio,i=()=>o();r.preload="auto",r.addEventListener("canplaythrough",i,{once:!0}),r.addEventListener("error",i,{once:!0}),r.src=e,r.load()})}function Pv({score:e,showNotes:o=!0,judgePack:r}){const{t:i}=Be();if(!e)return t.jsxs("div",{className:"score-empty",children:[t.jsx("span",{children:i("Score Judging")}),t.jsx("strong",{children:"--"})]});const c=bT(e).map(l=>{const p=Et.find(b=>b.key===l.key),f=r?.feedback?.[l.key]?.[l.tone];return{...l,label:r?.labels?.[l.key]??i(l.label),verdict:f??i(l.verdict),judgeName:r?.names?.[l.key]??p?.name??l.label,judgeImage:r?.images[l.key]??Cc[l.key]}});return t.jsxs("div",{className:"score-panel",children:[t.jsxs("div",{className:"score-total",children:[t.jsx("span",{children:i(e.rating)}),t.jsx("strong",{children:e.total})]}),t.jsx("div",{className:"judge-feedback-list","aria-label":i("Judge feedback"),children:c.map(l=>t.jsxs("div",{className:`judge-feedback-row ${l.tone}`,children:[t.jsx("img",{alt:"",className:"judge-feedback-avatar",decoding:"async",src:l.judgeImage,title:l.judgeName,onError:p=>{p.currentTarget.dataset.fallbackApplied||(p.currentTarget.dataset.fallbackApplied="true",p.currentTarget.src=Cc[l.key])}}),t.jsxs("div",{className:"judge-feedback-copy",children:[t.jsx("span",{children:l.label}),t.jsx("strong",{children:l.verdict})]})]},l.key))}),o?t.jsx("div",{className:"score-notes",children:e.notes.map(l=>t.jsx("span",{children:i(l)},l))}):null]})}const Cv=new WeakMap;function BT(e,o){const r=Math.max(1,Math.round(o)),i=Cv.get(e),c=i?.get(r);if(c)return c;let l=0;for(let w=0;w<e.length;w+=1)l=Math.max(l,Math.abs(e[w]));const p=new Float32Array(r),f=Math.max(1,Math.floor(e.length/r));for(let w=0;w<r;w+=1){const M=w*f;let E=0;for(let z=0;z<f&&M+z<e.length;z+=1)E=Math.max(E,Math.abs(e[M+z]));p[w]=E}const b={peak:l,columns:p},y=i??new Map;return y.set(r,b),i||Cv.set(e,y),b}function kd({samples:e,energy:o,label:r,className:i="",liveLevels:c,playerSamples:l,playerRevealProgress:p,progress:f,revealProgress:b,contentStartProgress:y,contentEndProgress:w,playerContentStartProgress:M,playerContentEndProgress:E,showCaption:z=!0}){const N=m.useRef(null),j=m.useRef({width:720,height:220,pixelRatio:1}),[D,I]=m.useState(0);return m.useEffect(()=>{const B=N.current;if(!B)return;let L;const _=()=>{L!==void 0&&window.cancelAnimationFrame(L),L=window.requestAnimationFrame(()=>{const W=Math.min(2,Math.max(1,window.devicePixelRatio||1)),O=Math.max(1,Math.round(B.getBoundingClientRect().width||720)),K=Math.max(1,Math.round(B.getBoundingClientRect().height||220)),T=j.current;T.width===O&&T.height===K&&T.pixelRatio===W||(j.current={width:O,height:K,pixelRatio:W},B.width=Math.round(O*W),B.height=Math.round(K*W),I(Y=>Y+1))})};_();const oe=typeof ResizeObserver>"u"?void 0:new ResizeObserver(_);return oe?.observe(B),window.addEventListener("resize",_),()=>{oe?.disconnect(),window.removeEventListener("resize",_),L!==void 0&&window.cancelAnimationFrame(L)}},[]),m.useEffect(()=>{const B=N.current;if(!B)return;const L=B.getContext("2d");if(!L)return;const{width:_,height:oe,pixelRatio:W}=j.current,O=Math.round(_*W),K=Math.round(oe*W);
43(B.width!==O||B.height!==K)&&(B.width=O,B.height=K),L.setTransform(W,0,0,W,0,0);const T=oe/2;L.clearRect(0,0,_,oe),L.fillStyle="#000",L.fillRect(0,0,_,oe);const Y=Math.max(0,Math.min(1,y??0))*_,X=Math.min(_,Math.max(Y+1,Math.max(0,Math.min(1,w??1))*_)),me=Math.max(0,Math.min(1,M??y??0))*_,We=Math.min(_,Math.max(me+1,Math.max(0,Math.min(1,E??w??1))*_));(Y>0||X<_)&&(L.fillStyle="rgba(247, 247, 255, 0.07)",Y>0&&L.fillRect(0,0,Y,oe),X<_&&L.fillRect(X,0,_-X,oe),L.strokeStyle="rgba(247, 247, 255, 0.18)",L.lineWidth=2,L.beginPath(),L.moveTo(Y,0),L.lineTo(Y,oe),L.moveTo(X,0),L.lineTo(X,oe),L.stroke()),L.strokeStyle="rgba(247, 247, 255, 0.12)",L.lineWidth=1,L.beginPath(),L.moveTo(0,T),L.lineTo(_,T),L.stroke();const vt=(kt,be)=>{const ce=oe*.44*(be.scale??1);L.save(),L.globalAlpha=be.opacity??1,L.strokeStyle=be.strokeStyle,L.lineWidth=be.lineWidth;const pe=Math.max(0,Math.min(_,be.startX??0)),Ie=Math.min(_,Math.max(pe+1,Math.min(_,be.endX??_))),Ht=Math.max(1,Math.round(Ie-pe));if(be.revealProgress!==void 0){const ke=Math.max(0,Math.min(1,be.revealProgress))*_;L.beginPath(),L.rect(0,0,ke,oe),L.clip()}L.beginPath();const q=BT(kt,Ht),co=be.absoluteScalePeak?1/be.absoluteScalePeak:q.peak>0?1/q.peak:1;for(let ke=0;ke<Ht;ke+=1){const te=q.columns[ke]??0,ae=Math.min(ce,Math.max(be.minAmplitude??1.5,te*co*ce)),It=pe+ke;L.moveTo(It,T-ae),L.lineTo(It,T+ae)}L.stroke(),L.restore()};if(e?.length&&vt(e,{strokeStyle:"#4ee7d2",lineWidth:2.5,revealProgress:b,startX:Y,endX:X}),l?.length&&(vt(l,{strokeStyle:"#ff8a6b",lineWidth:2,absoluteScalePeak:.32,minAmplitude:1,opacity:.6,scale:.82,startX:me,endX:We}),p!==void 0&&vt(l,{strokeStyle:"#ffd166",lineWidth:2.5,absoluteScalePeak:.32,minAmplitude:1,opacity:.95,revealProgress:p,scale:.9,startX:me,endX:We})),o?.length&&(L.strokeStyle="#ff8a6b",L.lineWidth=3,L.beginPath(),o.forEach((kt,be)=>{const ce=be/Math.max(1,o.length-1)*_,pe=oe-kt*oe*.8-12;be===0?L.moveTo(ce,pe):L.lineTo(ce,pe)}),L.stroke()),c?.length){const kt=oe*.42;L.save();const be=c.map(ce=>{const pe=Math.max(0,Math.min(1,ce.progress))*_,Ie=Math.max(2,Math.max(0,Math.min(1,ce.value))*kt);return{x:pe,amplitude:Ie}});L.fillStyle="rgba(255, 138, 107, 0.24)",L.beginPath(),be.forEach((ce,pe)=>{const Ie=T-ce.amplitude;pe===0?L.moveTo(ce.x,Ie):L.lineTo(ce.x,Ie)}),[...be].reverse().forEach(ce=>{L.lineTo(ce.x,T+ce.amplitude)}),L.closePath(),L.fill(),L.strokeStyle="rgba(255, 156, 118, 0.82)",L.lineWidth=2,L.beginPath(),be.forEach((ce,pe)=>{const Ie=T-ce.amplitude;pe===0?L.moveTo(ce.x,Ie):L.lineTo(ce.x,Ie)}),L.stroke(),L.beginPath(),be.forEach((ce,pe)=>{const Ie=T+ce.amplitude;pe===0?L.moveTo(ce.x,Ie):L.lineTo(ce.x,Ie)}),L.stroke(),L.restore()}if(f!==void 0){const be=Math.max(0,Math.min(1,f))*_;L.shadowColor="rgba(255, 255, 255, 0.45)",L.shadowBlur=8,L.strokeStyle="#f7f7ff",L.lineWidth=3,L.beginPath(),L.moveTo(be,0),L.lineTo(be,oe),L.stroke(),L.shadowBlur=0}},[e,o,c,l,p,f,b,y,w,M,E,D]),t.jsxs("figure",{className:["waveform-frame",i].filter(Boolean).join(" "),children:[t.jsx("canvas",{ref:N,width:720,height:220,"aria-label":r}),z?t.jsx("figcaption",{children:r}):null]})}const WT=/^ref_\d+(?:\.\d+)?_\d+(?:\.\d+)?$/i;function $T(e){const r=(e.replace(/\\/g,"/").split("/").pop()??e).replace(/\.[^.]+$/,"");return WT.test(r)}function nu(e){const o={root:{}};let r="root";for(const i of e.split(/\r?\n/)){const c=i.trim();if(!c||c.startsWith(";")||c.startsWith("#"))continue;const l=c.match(/^\[([^\]]+)]$/);if(l){r=l[1].trim().toLowerCase(),o[r]??={};continue}const p=c.indexOf("=");if(p<0)continue;const f=c.slice(0,p).trim().toLowerCase(),b=c.slice(p+1).trim();o[r]??={},o[r][f]=b}return o}function Xe(e,o,r=""){for(const i of o){const[c="root",l=""]=i.includes(".")?i.split("."):["root",i],p=e[c.toLowerCase()]?.[l.toLowerCase()];if(p)return GT(p)}return r}function GT(e){const o=e.trim();return o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'")?o.slice(1,-1).replaceAll('\\"','"').replaceAll("\\'","'"):o}function Wi(e,o=""){return e?.trim()||o.trim()}const gS={maxZipBytes:800*1024*1024,maxFiles:5e3,maxAudioSeconds:20},fh=new Set(["wav","mp3","ogg"]),qh=new Set(["png","jpg","jpeg","webp"]),bh=new Set(["mp4","webm","ogv","mov","m4v"]),fS=new Set(["txt"]),bS=new Set(["ini","cfg"]),Hh=new Set(["rar","7z"]),qT=new Set(["desktop.ini","thumbs.db",".ds_store"]),HT=new Set([...fh,...qh,...bh,...fS,...bS]),yh=["mp4","webm","m4v","mov","ogv"],JT=new Set(["cover","poster","thumbnail","preview"]),_T="Original ChoicerVoicer and GameBanana dubbing packs that still contain OGV video cannot be imported into Voxalike. Convert every OGV video in the pack to MP4, update any video filenames referenced by the pack if needed, then import the converted ZIP or folder.";
43class vh extends Error{constructor(){super(_T),this.name="UnsupportedLegacyDubPackError"}}async function bc(e,o=gS){if(e.size>o.maxZipBytes)throw new Error(`ZIP is too large. Maximum size is ${Math.round(o.maxZipBytes/1024/1024)}MB.`);if(Hh.has(Ka(e.name)))throw new Error("RAR/7Z archives need to be extracted first. Import the extracted folder instead.");const{default:r}=await Ko(async()=>{const{default:l}=await import("./jszip.min-CK9NPRCS.js").then(p=>p.j);return{default:l}},__vite__mapDeps([5,2,6])),i=await r.loadAsync(await e.arrayBuffer()),c=Object.values(i.files).filter(l=>!l.dir).map(vS);return yS(c,e.name.replace(/\.[^.]+$/,""),o)}async function YT(e){if(Hh.has(Ka(e.name)))return;const{default:o}=await Ko(async()=>{const{default:c}=await import("./jszip.min-CK9NPRCS.js").then(l=>l.j);return{default:c}},__vite__mapDeps([5,2,6])),r=await o.loadAsync(await e.arrayBuffer()),i=Object.values(r.files).filter(c=>!c.dir).map(vS);return wS(i)}async function Em(e,o=gS){const r=Array.from(e);if(r.reduce((l,p)=>l+p.size,0)>o.maxZipBytes)throw new Error(`Folder is too large. Maximum size is ${Math.round(o.maxZipBytes/1024/1024)}MB.`);const c=rE(r)??"local-folder";return yS(r.map(kS),c,o)}async function KT(e){return wS(Array.from(e).map(kS))}async function yS(e,o,r){if(e.length>r.maxFiles)throw new Error(`ZIP contains too many files. Maximum count is ${r.maxFiles}.`);const i=[],c=new Map,l=new Map;let p={root:{}},f,b,y,w,M,E=!1;const z=new Set;for(const W of e){const O=vu(W.name);if(!O){i.push(`Ignored unsafe path: ${W.name}`);continue}const K=Dn(O).toLowerCase();if(RS(O,K))continue;l.set(O,W);const T=Ka(K);if(Hh.has(T)){E=!0;continue}if(!HT.has(T))continue;if(K==="_pack_info.ini"){p=nu(await W.text()),b=Xe(p,["data.icon","pack.icon","root.icon"]),y=O,console.log("Found _pack_info.ini, icon field:",b);continue}if(K==="_author.txt"){f=(await W.text()).trim(),console.log("Found _author.txt:",f);continue}if(fh.has(T)&&sE(K)){M=W;continue}if(K.startsWith("_"))continue;if(bh.has(T)&&nE(K)){w=wh(w,W);continue}const Y=dE(O),X=c.get(Y)??{folder:PS(O),title:uE(Y)};fh.has(T)?X.audio=W:qh.has(T)?X.image=W:bh.has(T)?X.video=W:fS.has(T)?X.caption=W:bS.has(T)&&(X.ini=W),c.set(Y,X)}const N=SS(l,b,y),j=[],D=new Map,I=[];for(const[W,O]of c){if(!O.audio||cE(O))continue;let K=O.ini?nu(await O.ini.text()):{root:{}},T="";if(O.caption){const te=(await O.caption.text()).trim(),ae=eE(te);ae?(O.ini||(K=ae),T=Xe(ae,["data.caption","clip.caption","root.caption"],"")):T=te}const Y=Xe(K,["data.image","clip.image","root.image"]),X=Xe(K,["data.video","clip.video","root.video"]),me=O.image??(Y?l.get(kh(O.folder,Y)):void 0),We=X?iE(l,kh(O.folder,X)):void 0,Ae=wh(O.video,We)??w;if(Ae&&Ka(Ae.name)==="ogv")throw j.forEach(te=>URL.revokeObjectURL(te)),new vh;const vt=await O.audio.blob(),kt=URL.createObjectURL(vt);j.push(kt);let be,ce;me&&(be=await me.blob(),ce=URL.createObjectURL(be),j.push(ce));let pe,Ie;if(Ae){let te=D.get(Ae.name);te||(pe=await Ae.blob(),Ie=URL.createObjectURL(pe),j.push(Ie),te={file:pe,url:Ie},D.set(Ae.name,te)),pe=te.file,Ie=te.url}const Ht=Xe(K,["data.title","clip.title","root.title"],O.title),q=QT(Xe(K,["data.dub_characters","clip.dub_characters","root.dub_characters"]));q.forEach(te=>z.add(te));const co=lE(Xe(K,["data.dub_timestamps","clip.dub_timestamps","root.dub_timestamps"]))?.[0]??Sh(Xe(K,["data.start","data.start_time","data.video_start","clip.start","root.start"]))??jv(O.title)??jv(W),ke=Sh(Xe(K,["data.end","data.end_time","data.video_end","clip.end","root.end"]));I.push({id:Tv(W),title:Ht,caption:T||Xe(K,["data.caption","clip.caption","root.caption"],""),speakers:q,audioFile:vt,audioUrl:kt,imageFile:be,imageUrl:ce,videoFile:pe,videoUrl:Ie,videoStartSeconds:co,videoEndSeconds:ke,metadata:{sourcePath:O.audio.name,videoPath:Ae?.name??"",language:Xe(K,["data.language","clip.language","root.language"],"")}})}if(I.length===0){if(E)throw new Error("This folder contains a RAR/7Z archive. Extract the archive first, then import the extracted dub mod folder.");i.push("No playable WAV, MP3, or OGG clips were found.")}I.sort((W,O)=>{const K=W.videoStartSeconds??Number.POSITIVE_INFINITY,T=O.videoStartSeconds??Number.POSITIVE_INFINITY;return K-T||W.title.localeCompare(O.title,void 0,{numeric:!0})}),XT(I);let B,L;M&&(B=await M.blob(),L=URL.createObjectURL(B),j.push(L));let _,oe;return N?(_=await N.blob(),oe=URL.createObjectURL(_),j.push(oe),console.log("Pack icon created:",oe)):console.log("No pack icon found in pack"),{id:Tv(o)||"local-pack",title:Wi(Xe(p,["data.title","pack.title","root.title"]),o),creator:f||ZT(Xe(p,["data.authors","pack.creator","root.creator"],"")),characters:Array.from(z).sort(),language:Xe(p,["data.language","pack.language","root.language"],"Unknown"),tags:Xe(p,["data.tags","pack.tags","root.tags"],"").split(",").map(W=>
43W.trim()).filter(Boolean),iconFile:_,iconUrl:oe,source:"local-zip",backingTrackFile:B,backingTrackUrl:L,clips:I,warnings:i,release:()=>{j.forEach(W=>URL.revokeObjectURL(W))}}}function ZT(e){const o=e.trim();return o?o.startsWith("[")&&o.endsWith("]")?o.slice(1,-1).split(",").map(r=>r.trim().replace(/^["']|["']$/g,"")).filter(Boolean).join(", "):o:""}function XT(e){for(let o=0;o<e.length;o+=1){const r=e[o];if(r.videoEndSeconds!==void 0||r.videoStartSeconds===void 0)continue;const i=r.metadata.videoPath||r.videoUrl||"",c=e.slice(o+1).find(l=>{const p=l.metadata.videoPath||l.videoUrl||"";return l.videoStartSeconds!==void 0&&l.videoStartSeconds>r.videoStartSeconds&&p===i});c?.videoStartSeconds!==void 0&&(r.videoEndSeconds=c.videoStartSeconds)}}function QT(e){if(!e)return[];const o=e.trim();return o?o.startsWith("[")&&o.endsWith("]")?o.slice(1,-1).split(",").map(r=>r.trim().replace(/^["']|["']$/g,"")).filter(Boolean):o.split(",").map(r=>r.trim()).filter(Boolean):[]}function vS(e){return{name:e.name,blob:async()=>xS(await e.async("blob"),e.name),text:()=>e.async("text")}}function kS(e){const o=e.webkitRelativePath||e.name;return{name:o,blob:async()=>xS(e,o),text:()=>e.text()}}async function wS(e){const o=new Map;let r,i;for(const c of e){const l=vu(c.name);if(!l)continue;const p=Dn(l).toLowerCase();if(!RS(l,p)&&(o.set(l,c),p==="_pack_info.ini")){const f=nu(await c.text());r=Xe(f,["data.icon","pack.icon","root.icon"]),i=l}}return SS(o,r,i)?.blob()}function SS(e,o,r){const i=Array.from(e.entries()).map(([c,l])=>({path:c,entry:l,filename:Dn(c).toLowerCase(),stem:aE(Dn(c)).toLowerCase(),extension:Ka(c)})).filter(c=>qh.has(c.extension));if(i.length){if(o){const c=r?PS(r):"",l=vu(c?kh(c,o):o)?.toLowerCase(),p=Dn(o).toLowerCase(),f=l?i.find(y=>y.path.toLowerCase()===l):void 0,b=i.find(y=>y.filename===p);if(f??b)return(f??b)?.entry}return i.find(c=>c.filename==="_icon.png")?.entry??i.find(c=>c.filename.startsWith("_icon."))?.entry??i.find(c=>JT.has(c.stem))?.entry}}function eE(e){if(!tE(e))return;const o=nu(e);return!!Xe(o,["data.caption","clip.caption","root.caption"])||!!Xe(o,["data.image","clip.image","root.image"])||!!Xe(o,["data.dub_timestamps","clip.dub_timestamps","root.dub_timestamps"])?o:void 0}function tE(e){return/^\s*\[[^\]]+]/m.test(e)&&/^\s*[a-z0-9_.-]+\s*=/im.test(e)}function xS(e,o){const r=oE(o);return!r||e.type===r?e:new Blob([e],{type:r})}function oE(e){const o=Ka(e);return{jpg:"image/jpeg",jpeg:"image/jpeg",m4v:"video/x-m4v",mov:"video/quicktime",mp3:"audio/mpeg",mp4:"video/mp4",ogg:"audio/ogg",ogv:"video/ogg",png:"image/png",wav:"audio/wav",webm:"video/webm",webp:"image/webp"}[o]??""}function rE(e){return e[0]?.webkitRelativePath?.split(/[\\/]/).filter(Boolean)[0]}function vu(e){const o=e.replaceAll("\\","/").replace(/^\/+/,"");if(!o||o.includes("\0")||o.includes("../")||o.startsWith("../")||/^[a-z]:/i.test(o))return null;const r=o.split("/").filter(Boolean);return r.some(i=>i==="."||i==="..")?null:r.join("/")}function Dn(e){return e.split("/").at(-1)??e}function RS(e,o=Dn(e).toLowerCase()){return e.split("/").some(i=>i.toLowerCase()==="__macosx")||o.startsWith("._")||qT.has(o)}function PS(e){const o=e.split("/");return o.length>1?o.slice(0,-1).join("/"):""}function aE(e){return e.replace(/\.[^.]+$/,"")}function kh(e,o){const r=o.replaceAll("\\","/").replace(/^\/+/,"");return e?`${e}/${r}`:r}function Ka(e){return e.split(".").at(-1)?.toLowerCase()??""}function nE(e){const o=e.replace(/\.[^.]+$/,"").toLowerCase();return o==="dub_video"||o==="video"||o==="pack_video"||o==="mod_video"}function wh(e,o){return e?o&&Mv(o.name)<Mv(e.name)?o:e:o}function Mv(e){const o=yh.indexOf(Ka(e));return o>=0?o:yh.length}function iE(e,o){const r=vu(o);if(!r)return;const i=e.get(r),c=Ka(r),l=r.replace(/\.[^.]+$/,""),p=yh.filter(f=>f!==c).map(f=>e.get(`${l}.${f}`));return[i,...p].reduce((f,b)=>wh(f,b),void 0)}function sE(e){const o=e.replace(/\.[^.]+$/,"").toLowerCase();return o==="_backing_track"||o==="backing_track"||o==="_backing"||o==="backing"}function cE(e){if(!e.audio||e.ini||e.caption||e.image||e.video)return!1;const o=Dn(e.audio.name).replace(/\.[^.]+$/,"").toLowerCase().replace(/[\s_]+/g,"-");return $T(e.audio.name)||["vocals","vocal","instrumental","instrumentals","no-vocals","novocals","karaoke"].includes(o)}function Sh(e){if(!e)return;const o=e.trim().replace(/^["']|["']$/g,""),r=Number(o);if(Number.isFinite(r))return r;const i=o.split(":").map(Number);if(i.length>=2&&i.every(Number.isFinite))return i.reduce((c,l)=>c*60+l,0)}function lE(e){if(!e)return;const r=e.trim().replace(/^\[|\]$/g,"").split(",").map(i=>Sh(i)).filter(i=>typeof i=="number");return r.length?r:void 0}function jv(e){const o=e.match(/(?:^|[^0-9])(\d{1,4})[\s_-](\d{3})(?:\D*)$/);if(!o)return;const r=Number(o[1]),i=Number(o[2]);if(!(!Number.isFinite(r)||!Number.isFinite(i)))return r+i/1e3}function dE(e){const o=e.split("/"),i=(o.at(-1)??e).replace(/\.[^.]+$/,"");return o.length>1?[...o.slice(0,-1).slice(-1),i].join("/"):i}function uE(e){return e.split("/").at(-1).replace(/[-_]+/g," ").replace(/\b\w/g,o=>o.toUpperCase())}function Tv(e){return e.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,80)}const pE=5e3,CS=2*1024*1024;async function mE(e){if(yc(e.name)!=="zip")throw new Error("Choose a Dub Mod ZIP exported by Voxalike Mod Maker.");const{default:o}=await Ko(async()=>{const{default:D}=await import("./jszip.min-CK9NPRCS.js").then(I=>I.j);return{default:D}},__vite__mapDeps([5,2,6])),r=await o.loadAsync(await e.arrayBuffer(),{checkCRC32:!0}),i=Object.values(r.files).filter(D=>!D.dir),c=i.find(D=>Qr(D.name).toLowerCase()==="voxalike_maker.json");if(!c)throw new Error("This ZIP has no Voxalike Mod Maker project data. Only ZIPs exported by Mod Maker can be reopened.");const l=await c.async("uint8array");if(l.byteLength>CS)throw new Error("The Mod Maker project data is too large to open safely.");const p=hE(new TextDecoder().decode(l)),f=Ev(c.name),b=i.filter(D=>Ev(D.name)===f),y=Am(b,f,p.storedFiles?.source)??b.find(D=>Qr(D.name).toLowerCase()==="dub_video.mp4")??b.find(D=>yc(D.name)==="mp4");if(!y||yc(y.name)!=="mp4")throw new Error("The project ZIP does not contain its MP4 source video.");const w=Am(b,f,p.storedFiles?.cover)??b.find(D=>/^cover\.(png|jpe?g|webp)$/i.test(Qr(D.name))),M=Am(b,f,p.storedFiles?.backing||p.backingTrack)??b.find(D=>/^_backing_track\.(mp3|wav|ogg|flac)$/i.test(Qr(D.name))),E=await wd(y),z=w?await wd(w):void 0,N=M?await wd(M):void 0,j=await Promise.all(p.lines.map(async(D,I)=>{const B=`${String(I+1).padStart(3,"0")}_`,L=b.find(_=>{const oe=Qr(_.name).toLowerCase();return oe.startsWith(B)&&yc(oe)==="wav"});return{...D,referenceAudio:L?await wd(L):void 0}}));return{title:p.title,subtitle:p.subtitle??"",author:p.author??"",sourceFile:E,coverFile:z,backingTrack:N,lines:j}}function hE(e){if(new TextEncoder().encode(e).byteLength>CS)throw new Error("The Mod Maker project data is too large to open safely.");let o;try{o=JSON.parse(e)}catch{throw new Error("The Mod Maker project data is damaged.")}if(!Im(o))throw new Error("The Mod Maker project data is damaged.");const r=Number(o.schemaVersion);if(!Number.isInteger(r)||r<1||r>2)throw new Error("This Mod Maker project version is not supported.");
43if(!Array.isArray(o.lines)||!o.lines.length||o.lines.length>pE)throw new Error("The Mod Maker project has no valid spoken lines.");const i=o.lines.map((c,l)=>{if(!Im(c))throw new Error(`Line ${l+1} in the project is damaged.`);const p=Number(c.start),f=Number(c.end);if(!Number.isFinite(p)||!Number.isFinite(f)||p<0||f<=p)throw new Error(`Line ${l+1} has invalid timing.`);return{start:p,end:f,character:Po(c.character,60,"Character"),caption:Po(c.caption,300),filename:Po(c.filename,64,`line_${String(l+1).padStart(2,"0")}`)}});return{schemaVersion:r,title:Po(o.title,80,"Untitled Dub Mod"),subtitle:Po(o.subtitle,120),author:Po(o.author,80),sourceFile:Po(o.sourceFile,180),voiceTrack:Po(o.voiceTrack,180),backingTrack:Po(o.backingTrack,180),storedFiles:Im(o.storedFiles)?{source:Po(o.storedFiles.source,180),cover:Po(o.storedFiles.cover,180),backing:Po(o.storedFiles.backing,180)}:void 0,lines:i}}function Am(e,o,r){if(!r)return;const i=r.replaceAll("\\","/").replace(/^\/+/,""),c=o?`${o}/${i}`:i;return e.find(l=>l.name.toLowerCase()===c.toLowerCase())??e.find(l=>Qr(l.name).toLowerCase()===Qr(i).toLowerCase())}async function wd(e){const o=Qr(e.name),r=await e.async("blob");return new File([r],o,{type:gE(o)})}function gE(e){const o=yc(e);return o==="mp4"?"video/mp4":o==="png"?"image/png":o==="jpg"||o==="jpeg"?"image/jpeg":o==="webp"?"image/webp":o==="wav"?"audio/wav":o==="mp3"?"audio/mpeg":o==="ogg"?"audio/ogg":o==="flac"?"audio/flac":"application/octet-stream"}function Po(e,o,r=""){return typeof e=="string"&&e.trim().slice(0,o)||r}function Im(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}function yc(e){const o=Qr(e),r=o.lastIndexOf(".");return r>=0?o.slice(r+1).toLowerCase():""}function Qr(e){return e.replaceAll("\\","/").split("/").pop()??""}function Ev(e){const o=e.replaceAll("\\","/"),r=o.lastIndexOf("/");return r>=0?o.slice(0,r):""}function fE(e,o){if(!Number.isFinite(e)||e<=0)return"The selected audio file could not be read.";if(!Number.isFinite(o)||o<=0)return;const r=Math.max(1,o*.02);if(Math.abs(e-o)>r)return`This track is ${Av(e)} long, but the video is ${Av(o)}.`}function Av(e){const o=Math.max(0,Math.round(e)),r=Math.floor(o/60),i=o%60;return`${r}:${String(i).padStart(2,"0")}`}const Iv=20,bE=2;function Nv(){const[e,o]=m.useState("Untitled Dub Mod"),[r,i]=m.useState(""),[c,l]=m.useState(""),[p,f]=m.useState(),[b,y]=m.useState(""),[w,M]=m.useState(),[E,z]=m.useState(),[N,j]=m.useState(),[D,I]=m.useState(""),[B,L]=m.useState(""),[_,oe]=m.useState("voxalike"),[W,O]=m.useState({running:!1,phase:"preparing",progress:0}),[K,T]=m.useState(!1),[Y,X]=m.useState("checking"),[me,We]=m.useState(0),[Ae,vt]=m.useState(0),[kt,be]=m.useState(!1),[ce,pe]=m.useState(!1),[Ie,Ht]=m.useState([]),[q,co]=m.useState(),[ke,te]=m.useState({tone:"info",text:"Choose an MP4 scene to begin. Everything stays in your browser."}),[ae,It]=m.useState(!1),[ut,lo]=m.useState(0),[ta,uo]=m.useState(!1),[Xa,oa]=m.useState([]),[vr,To]=m.useState(!1),[wt,ra]=m.useState(!1),[Eo,po]=m.useState(""),he=m.useRef(null),mo=m.useRef(void 0),Ee=m.useRef(void 0),Ao=m.useRef(void 0),Jt=m.useRef(void 0),ct=m.useMemo(()=>[...Ie].sort((V,Z)=>V.start-Z.start||V.end-Z.end),[Ie]),ze=Ie.find(V=>V.id===q)??ct[0],aa=m.useMemo(()=>RE(ct,me),[me,ct]);m.useEffect(()=>()=>{b&&URL.revokeObjectURL(b)},[b]),m.useEffect(()=>{let V=!0;const Z=navigator.gpu;return Z?Z.requestAdapter().then(ne=>{V&&X(ne?"available":"unavailable")}).catch(()=>{V&&X("unavailable")}):X("unavailable"),()=>{V=!1,Ee.current?.terminate(),Jt.current!==void 0&&window.clearTimeout(Jt.current)}},[]),m.useEffect(()=>{if(!E){I("");return}const V=URL.createObjectURL(E);return I(V),()=>URL.revokeObjectURL(V)},[E]),m.useEffect(()=>{if(!N){L("");return}const V=URL.createObjectURL(N);return L(V),()=>URL.revokeObjectURL(V)},[N]);function kr(V){if(!V)return;if(MS(V.name)!=="mp4"&&V.type!=="video/mp4"){te({tone:"error",text:"Use an MP4 source video. Voxalike exports MP4-based Dub Mods."});return}b&&URL.revokeObjectURL(b),Ee.current?.terminate(),Ee.current=void 0,Ao.current=void 0;const ne=URL.createObjectURL(V);f(V),y(ne),z(void 0),j(void 0),O({running:!1,phase:"preparing",progress:0}),We(0),vt(0),pe(!1),Ht([]),co(void 0),te({tone:V.size>250*1024*1024?"warning":"info",text:V.size>250*1024*1024?"Large video loaded. Export may use significant memory, so keep this tab open.":"Video loaded. Use Start and End to snap to the exact edges, or move the playhead to a spoken line."})}async function Fn(V){if(V)try{uo(!0),te({tone:"info",text:"Opening the Mod Maker project locally..."});const Z=await mE(V);b&&URL.revokeObjectURL(b),Ee.current?.terminate(),Ee.current=void 0,Ao.current=void 0;const ne=URL.createObjectURL(Z.sourceFile),Se=Z.lines.map(Me=>({id:crypto.randomUUID(),start:Me.start,end:Me.end,character:Me.character,caption:Me.caption,filename:Me.filename,referenceAudio:Me.referenceAudio,referenceStart:Me.start,referenceEnd:Me.end}));o(Z.title),l(Z.subtitle),i(Z.author),f(Z.sourceFile),y(ne),M(Z.coverFile),z(void 0),j(Z.backingTrack),O({running:!1,phase:"preparing",progress:0}),We(0),vt(0),pe(!1),Ht(Se),co(Se[0]?.id);
43const Nt=Se.filter(Me=>Me.referenceAudio).length;te({tone:Nt===Se.length?"success":"warning",text:Nt===Se.length?`Project opened with ${Se.length} editable lines.`:`Project opened with ${Se.length} lines. Missing reference clips will be rebuilt from the MP4 when exported.`})}catch(Z){te({tone:"error",text:Z instanceof Error?Z.message:"The Mod Maker project could not be opened."})}finally{uo(!1)}}async function ho(){if(vr){To(!1);return}if(To(!0),!Xa.length)try{ra(!0);const V=await fetch("/workshop/mod-maker/projects",{credentials:"same-origin",headers:{Accept:"application/json"}}),Z=await V.json();if(!V.ok)throw new Error(Z.error||"Your uploaded mods could not be loaded.");const ne=Array.isArray(Z.projects)?Z.projects:[];oa(ne),po(ne[0]?.slug??""),ne.length||te({tone:"info",text:"You do not have any uploaded Dub Mods yet."})}catch(V){To(!1),te({tone:"error",text:V instanceof Error?V.message:"Your uploaded mods could not be loaded."})}finally{ra(!1)}}async function Bn(){if(Eo)try{uo(!0),te({tone:"info",text:"Loading your uploaded Dub Mod..."});const V=await fetch(`/workshop/mod-maker/projects/${encodeURIComponent(Eo)}`,{credentials:"same-origin"});if(!V.ok){const ne=await V.json().catch(()=>({}));throw new Error(ne.error||"The uploaded ZIP could not be loaded.")}const Z=await V.blob();await Fn(new File([Z],`${Eo}.zip`,{type:"application/zip"})),To(!1)}catch(V){te({tone:"error",text:V instanceof Error?V.message:"The uploaded ZIP could not be loaded."})}finally{uo(!1)}}async function wr(V=0,Z=1){if(!p)throw new Error("Choose an MP4 scene first.");const ne=window.AudioContext??window.webkitAudioContext;if(!ne)throw new Error("Audio processing is not supported in this browser. Try Chrome or Edge.");const Se=new ne;try{const Nt=await Se.decodeAudioData(await p.arrayBuffer());return await vE(Nt,44100,Me=>{O(Oe=>({...Oe,phase:"preparing",progress:V+Me*(Z-V)}))})}finally{await Se.close().catch(()=>{})}}async function Wn(){if(!p){te({tone:"error",text:"Choose an MP4 scene before separating its audio."});return}if(Y!=="available"){te({tone:"error",text:"Voice separation requires Chrome or Edge with hardware acceleration enabled."});return}const V=Ee.current??new Worker(new URL("/assets/dub-separation.worker-BBBmnJsQ.js",import.meta.url),{type:"module"}),Z=crypto.randomUUID();Ee.current=V,Ao.current=Z,O({running:!0,phase:"preparing",progress:.01}),te({tone:"info",text:"Preparing the scene audio locally..."});try{const ne=await wr(.01,.06);if(Ao.current!==Z)return;const Se=await new Promise((Me,Oe)=>{const Rr=_t=>{const Ke=_t.data;if(Ke.jobId===Z){if(Ke.type==="progress"){O({running:!0,phase:Ke.phase,progress:Ke.progress,currentChunk:Ke.currentChunk,totalChunks:Ke.totalChunks,loadedBytes:Ke.loadedBytes,totalBytes:Ke.totalBytes});return}V.removeEventListener("message",Rr),V.removeEventListener("error",Ce),Ke.type==="result"?Me(Ke):Oe(Object.assign(new Error(Ke.message),{separationCode:Ke.code}))}},Ce=_t=>{V.removeEventListener("message",Rr),V.removeEventListener("error",Ce),Oe(new Error(_t.message||"The separation worker stopped unexpectedly."))};V.addEventListener("message",Rr),V.addEventListener("error",Ce),V.postMessage({type:"separate",jobId:Z,left:ne.left,right:ne.right,sampleRate:ne.sampleRate},[ne.left.buffer,ne.right.buffer])});if(Ao.current!==Z)return;const Nt=Ud(p.name)||"scene";z(new File([Se.voice],`${Nt}_voice.wav`,{type:"audio/wav"})),j(new File([Se.background],`${Nt}_background.wav`,{type:"audio/wav"})),O({running:!1,phase:"encoding",progress:1}),te({tone:"success",text:"Voice and background are ready. Preview them before exporting."})}catch(ne){if(Ao.current!==Z)return;V.terminate(),Ee.current=void 0;const Se=ne instanceof Error?ne.message:"Voice separation failed in this browser.";O({running:!1,phase:"preparing",progress:0}),te({tone:"error",text:`${Se} You can still use an external separator.`})}finally{Ao.current===Z&&(Ao.current=void 0)}}function Zo(){Ee.current?.terminate(),Ee.current=void 0,Ao.current=void 0,O({running:!1,phase:"preparing",progress:0}),te({tone:"warning",text:"Voice separation was cancelled."})}
43async function $n(){if(!p){te({tone:"error",text:"Choose an MP4 scene first."});return}try{T(!0),te({tone:"info",text:"Preparing a WAV file for the external separator..."});const V=await wr(),Z=g0(V.left,V.right,V.sampleRate);Uv(Z,`${Ud(p.name)}_source.wav`),te({tone:"success",text:"Source audio downloaded. Import the separated tracks when they are ready."})}catch(V){te({tone:"error",text:V instanceof Error?V.message:"Source audio could not be prepared."})}finally{T(!1),O({running:!1,phase:"preparing",progress:0})}}async function Ki(V,Z){if(Z)try{const ne=await kE(Z),Se=fE(ne,me);if(Se)throw new Error(Se);V==="voice"?z(Z):j(Z),te({tone:"success",text:`${V==="voice"?"Voice":"Background"} track imported and checked.`})}catch(ne){te({tone:"error",text:ne instanceof Error?ne.message:"The audio track could not be read."})}}function na(V){const Z=he.current;if(!Z||!me)return;const ne=Math.max(0,Math.min(V,me));if(mo.current=void 0,Z.pause(),!Z.seeking&&Math.abs(Z.currentTime-ne)<.001){vt(ne),pe(!1);return}pe(!0),Z.currentTime=ne,Jt.current!==void 0&&window.clearTimeout(Jt.current),Jt.current=window.setTimeout(()=>{Z.seeking&&(pe(!1),te({tone:"warning",text:"This MP4 is taking too long to seek. Re-encode or remux it before marking lines."}))},6e3)}function ia(){Jt.current!==void 0&&(window.clearTimeout(Jt.current),Jt.current=void 0),pe(!1),he.current&&vt(he.current.currentTime)}function Sr(){if(!p||!me){te({tone:"error",text:"Load an MP4 video before adding lines."});return}if(ce||he.current?.seeking){te({tone:"warning",text:"Wait for the video to finish seeking before marking a line."});return}const V=Dm(Math.min(Ae,Math.max(0,me-.1))),Z=Dm(Math.min(me,V+bE));Xo(V,Math.max(V+.1,Z)),te({tone:"info",text:"Line added. Set its end time, character and caption."})}function Xo(V,Z){const ne=crypto.randomUUID(),Se=Ie.length+1,Nt={id:ne,start:V,end:Z,character:"Character",caption:"",filename:`line_${String(Se).padStart(2,"0")}`};Ht(Me=>[...Me,Nt]),co(ne)}function Zi(V){const Z=V==="start"?0:me;na(Z),te({tone:"info",text:`Playhead snapped to the ${V} of the video (${Fa(Z)}).`})}function Io(V,Z){Ht(ne=>ne.map(Se=>Se.id===V?{...Se,...Z}:Se))}function sa(V){Ht(Z=>Z.filter(ne=>ne.id!==V)),q===V&&co(void 0)}function Xi(V){if(!ze)return;if(ce||he.current?.seeking){te({tone:"warning",text:"Wait for the video to finish seeking before saving this time."});return}const Z=Dm(Ae);Io(ze.id,{[V]:Z}),te({tone:"info",text:`${V==="start"?"Start":"End"} set to ${Fa(Z)} for ${ze.character||"the selected line"}.`})}async function Vc(V){const Z=he.current;if(Z)try{Math.abs(Z.currentTime-V.start)>.04&&(pe(!0),await wE(Z,V.start),ia()),mo.current=V.end,await Z.play()}catch{ia(),te({tone:"error",text:"The browser could not play this part of the source video."})}}function ca(){const V=he.current;V&&(vt(V.currentTime),mo.current!==void 0&&V.currentTime>=mo.current&&(V.pause(),mo.current=void 0))}async function xr(){if(!p){te({tone:"error",text:"Choose an MP4 video before exporting."});return}if(!e.trim()){te({tone:"error",text:"Add a title before exporting."});return}if(!ct.length){te({tone:"error",text:"Add at least one spoken line before exporting."});return}if(aa.length){te({tone:"error",text:aa[0]});return}const V=window.AudioContext??window.webkitAudioContext;if(!V){te({tone:"error",text:"Audio export is not supported in this browser. Try Chrome or Edge."});return}let Z;try{It(!0),lo(5),te({tone:"info",text:"Reading the reference audio locally..."}),Z=new V;const ne=await(E??p).arrayBuffer();lo(12);const Se=await Z.decodeAudioData(ne);if(ct.find(xe=>xe.end>Se.duration+.05))throw new Error("A marked line ends after the source audio. Shorten that line and try again.");const{default:Me}=await Ko(async()=>{const{default:xe}=await import("./jszip.min-CK9NPRCS.js").then(No=>No.j);return{default:xe}},__vite__mapDeps([5,2,6])),Oe=new Me,Rr=Ov(e),Ce=Oe.folder(Rr)??Oe,_t=w?`cover.${zv(w,"png")}`:"",Ke=N?`_backing_track.${zv(N,"mp3")}`:"";Ce.file("dub_video.mp4",p),w&&_t&&Ce.file(_t,w),N&&Ke&&Ce.file(Ke,N),r.trim()&&Ce.file("_author.txt",`${r.trim()} 44`),Ce.file("_pack_info.ini",["[data]",`title=${cc(e.trim())}`,c.trim()?`subtitle=${cc(c.trim())}`:"",`authors=${JSON.stringify([r.trim()||"Unknown Creator"])}`,_t?`icon=${cc(_t)}
44`:"",""].filter(Boolean).join(` 45`)),ct.forEach((xe,No)=>{const H=`${String(No+1).padStart(3,"0")}_${Ud(xe.filename||xe.character||"line")}`;if(xe.referenceAudio&&Math.abs(xe.start-(xe.referenceStart??-1))<5e-4&&Math.abs(xe.end-(xe.referenceEnd??-1))<5e-4)Ce.file(`${H}.wav`,xe.referenceAudio);else{const Le=xE(Se,xe.start,xe.end);Ce.file(`${H}.wav`,En(Le))}Ce.file(`${H}.ini`,["[data]",`title=${cc(xe.caption.trim()||`Line ${No+1}`)}`,`caption=${cc(xe.caption.trim())}`,`dub_timestamps=[${xe.start.toFixed(3)}]`,`start=${xe.start.toFixed(3)}`,`end=${xe.end.toFixed(3)}`,`dub_characters=${JSON.stringify([xe.character.trim()||"Character"])}`,""].join(` 46`)),lo(15+Math.round((No+1)/ct.length*45))}),Ce.file("voxalike_maker.json",JSON.stringify({schemaVersion:2,title:e.trim(),subtitle:c.trim(),author:r.trim(),sourceFile:p.name,voiceTrack:E?.name??"",backingTrack:N?.name??"",storedFiles:{source:"dub_video.mp4",cover:_t,backing:Ke},createdAt:new Date().toISOString(),lines:ct.map(({start:xe,end:No,character:Oc,caption:H,filename:Qi})=>({start:xe,end:No,character:Oc,caption:H,filename:Qi}))},null,2)),te({tone:"info",text:"Building and checking the Dub Mod ZIP..."});const la=await Oe.generateAsync({type:"blob",compression:"STORE",streamFiles:!0},xe=>lo(60+Math.round(xe.percent*.3))),zc=new File([la],`${Ov(e)}.zip`,{type:"application/zip"}),P=await bc(zc),Qa=P.clips.length;if(P.release(),Qa!==ct.length)throw new Error(`Pack check found ${Qa} of ${ct.length} lines.`);lo(100),Uv(la,zc.name),te({tone:"success",text:`${Qa} lines checked. Your Voxalike Dub Mod ZIP is ready.`})}catch(ne){te({tone:"error",text:ne instanceof Error?`Export failed: ${ne.message}`:"Export failed in this browser."})}finally{await Z?.close().catch(()=>{}),It(!1)}}return t.jsxs("section",{className:"dub-maker-shell",children:[t.jsxs("aside",{className:"dub-maker-sidebar panel",children:[t.jsxs("div",{className:"dub-maker-project-actions",children:[t.jsx(ic,{accept:"application/zip,.zip",icon:t.jsx(oh,{size:18}),label:ta?"Opening...":"Open ZIP",onChange:V=>{Fn(V)}}),t.jsxs("button",{className:"dub-maker-file-field",disabled:wt||ta,type:"button",onClick:()=>{ho()},children:[t.jsx(Gt,{size:18}),t.jsx("span",{children:wt?"Loading...":"My Mods"})]})]}),vr?t.jsx("div",{className:"dub-maker-project-picker",children:Xa.length?t.jsxs(t.Fragment,{children:[t.jsx("select",{"aria-label":"Choose one of your uploaded Dub Mods",value:Eo,onChange:V=>po(V.target.value),children:Xa.map(V=>t.jsx("option",{value:V.slug,children:V.title},V.slug))}),t.jsx("button",{className:"action-button",disabled:!Eo||ta,type:"button",onClick:()=>{Bn()},children:ta?"Opening...":"Open"})]}):wt?null:t.jsx("span",{children:"No uploaded Dub Mods"})}):null,t.jsx("div",{className:"dub-maker-divider"}),t.jsxs("div",{className:"dub-maker-section-heading",children:[t.jsx("span",{children:"1 · SOURCE"}),t.jsx("strong",{children:"Scene assets"})]}),t.jsx(ic,{accept:"video/mp4,.mp4",icon:t.jsx(BC,{size:18}),label:p?p.name:"Choose MP4 scene",onChange:V=>kr(V)}),t.jsx(ic,{accept:"image/png,image/jpeg,image/webp",icon:t.jsx(Ld,{size:18}),label:w?w.name:"Optional cover",onChange:M}),t.jsx("div",{className:"dub-maker-divider"}),t.jsxs("div",{className:"dub-maker-section-heading",children:[t.jsx("span",{children:"2 · PACK INFO"}),t.jsx("strong",{children:"Workshop details"})]}),t.jsx(sc,{label:"Title",maxLength:80,value:e,onChange:o}),t.jsx(sc,{label:"Creator",maxLength:80,placeholder:"Your name",value:r,onChange:i}),t.jsx(sc,{label:"Subtitle",maxLength:120,optional:!0,value:c,onChange:l}),t.jsxs("div",{className:"dub-maker-export",children:[t.jsxs("button",{className:"action-button",disabled:ae||!p,type:"button",onClick:()=>{xr()},children:[t.jsx(Gt,{size:18}),ae?`Building ${ut}%`:"Export Dub Mod ZIP"]}),ae?t.jsx("div",{className:"dub-maker-progress","aria-label":`Export ${ut}%`,children:t.jsx("span",{style:{width:`${ut}%`}})}):null,t.jsx("small",{children:"MP4 and line audio are processed locally. Nothing is uploaded."})]})]}),t.jsxs("div",{className:"dub-maker-workspace",children:[t.jsx("div",{className:"dub-maker-player panel",children:b?t.jsx("video",{controls:!0,onDurationChange:V=>We(Number.isFinite(V.currentTarget.duration)?V.currentTarget.duration:0),onEnded:()=>be(!1),onError:()=>te({tone:"error",text:"This MP4 could not be decoded by the browser."}),onPause:()=>be(!1),onPlay:()=>be(!0),onSeeked:ia,onSeeking:()=>
46pe(!0),onTimeUpdate:ca,playsInline:!0,ref:he,src:b}):t.jsxs("label",{className:"dub-maker-empty-player",children:[t.jsx(oh,{size:34}),t.jsx("strong",{children:"Drop in the scene you want to dub"}),t.jsx("span",{children:"Choose one MP4 video. Its audio becomes the reference for each line."}),t.jsx("input",{accept:"video/mp4,.mp4",hidden:!0,type:"file",onChange:V=>kr(V.target.files?.[0])})]})}),t.jsxs("div",{className:"dub-maker-separation panel",children:[t.jsxs("div",{className:"dub-maker-separation-head",children:[t.jsxs("div",{children:[t.jsx(Sy,{size:18}),t.jsx("strong",{children:"Voice & Background"})]}),N?t.jsx("span",{children:"Background ready"}):null]}),t.jsxs("div",{className:"dub-maker-separation-tabs",role:"tablist","aria-label":"Audio separation method",children:[t.jsx("button",{"aria-selected":_==="voxalike",className:_==="voxalike"?"active":"",role:"tab",type:"button",onClick:()=>oe("voxalike"),children:"Separate in Voxalike"}),t.jsx("button",{"aria-selected":_==="external",className:_==="external"?"active":"",role:"tab",type:"button",onClick:()=>oe("external"),children:"Use External Tool"})]}),_==="voxalike"?t.jsxs("div",{className:"dub-maker-separation-method",children:[Y==="available"?t.jsx("span",{className:"dub-maker-capability ready",children:"Chrome/Edge · Local WebGPU"}):Y==="checking"?t.jsx("span",{className:"dub-maker-capability",children:"Checking WebGPU..."}):t.jsx("span",{className:"dub-maker-capability unavailable",children:"WebGPU unavailable"}),W.running?t.jsxs("div",{className:"dub-maker-separation-running",children:[t.jsxs("div",{children:[t.jsx("span",{children:yE(W)}),t.jsxs("strong",{children:[Math.round(W.progress*100),"%"]})]}),t.jsx("div",{className:"dub-maker-progress","aria-label":`Voice separation ${Math.round(W.progress*100)}%`,children:t.jsx("span",{style:{width:`${Math.round(W.progress*100)}%`}})}),t.jsx("button",{className:"ghost-button",type:"button",onClick:Zo,children:"Cancel"})]}):t.jsxs("button",{className:"action-button",disabled:!p||Y!=="available",type:"button",onClick:()=>{Wn()},children:[t.jsx(WC,{size:17})," Separate Voice & Background"]})]}):t.jsxs("div",{className:"dub-maker-separation-method external",children:[t.jsxs("div",{className:"dub-maker-external-actions",children:[t.jsxs("button",{className:"ghost-button",disabled:!p||K,type:"button",onClick:()=>{$n()},children:[t.jsx(Gt,{size:16})," ",K?"Preparing WAV...":"Download Source Audio"]}),t.jsxs("a",{className:"ghost-button",href:"https://ultimatevocalremover.com/",rel:"noreferrer",target:"_blank",children:["Open UVR ",t.jsx($C,{size:15})]})]}),t.jsxs("div",{className:"dub-maker-separated-imports",children:[t.jsx(ic,{accept:"audio/mpeg,audio/wav,audio/ogg,audio/flac,.mp3,.wav,.ogg,.flac",icon:t.jsx(dw,{size:17}),label:E?"Replace voice track":"Import voice track",onChange:V=>{Ki("voice",V)}}),t.jsx(ic,{accept:"audio/mpeg,audio/wav,audio/ogg,audio/flac,.mp3,.wav,.ogg,.flac",icon:t.jsx(Sy,{size:17}),label:N?"Replace background track":"Import background track",onChange:V=>{Ki("background",V)}})]})]}),E||N?t.jsxs("div",{className:"dub-maker-separated-tracks",children:[E&&D?t.jsx(Dv,{label:"Voice",file:E,url:D,onRemove:()=>z(void 0)}):null,N&&B?t.jsx(Dv,{label:"Background",file:N,url:B,onRemove:()=>j(void 0)}):null]}):null]}),t.jsxs("div",{className:"dub-maker-timeline panel",children:[t.jsxs("div",{className:"dub-maker-timeline-toolbar",children:[t.jsx("button",{className:"ghost-button icon-button",disabled:!b||ce,type:"button",title:kt?"Pause":"Play",onClick:()=>{const V=he.current;V&&(V.paused?V.play():V.pause())},children:kt?t.jsx(GC,{size:18}):t.jsx(_e,{size:18})}),t.jsx("strong",{children:Fa(Ae)}),t.jsxs("span",{children:["/ ",Fa(me)," ",ce?t.jsx("em",{className:"dub-maker-seeking",children:"Seeking..."}):null]}),t.jsxs("div",{className:"dub-maker-timeline-markers","aria-label":"Snap to video edges",children:[t.jsx("button",{className:"ghost-button",disabled:!b||ce,type:"button",onClick:()=>Zi("start"),children:"Start"}),t.jsx("button",{className:"ghost-button",disabled:!b||ce,type:"button",onClick:()=>Zi("end"),children:"End"})]}),t.jsxs("button",{className:"action-button",disabled:!b||ce,type:"button",onClick:Sr,children:[t.jsx(xy,{size:18})," Add Line Here"]})]}),t.jsxs("div",{className:"dub-maker-track","aria-label":"Dub line timeline",onClick:V=>{if(!he.current||!me)return;const ne=V.currentTarget.getBoundingClientRect();na((V.clientX-ne.left)/ne.width*me)},children:[t.jsx("div",{className:"dub-maker-track-grid"}),me>0?t.jsx("span",{className:"dub-maker-playhead",style:{left:Ae>=me?"calc(100% - 2px)":`${Nm(Ae,me)}%`}}):null,me>0?ct.map((V,Z)=>t.jsx("button",{className:`dub-maker-line-block ${V.id===ze?.id?"active":""}`,style:{left:`${Nm(V.start,me)}%`,width:`${Math.max(.7,Nm(V.end-V.start,me))}%`,top:`${10+Z%3*30}px`},title:`${V.character}: ${Fa(V.start)}-${Fa(V.end)}`,type:"button",onClick:ne=>{ne.stopPropagation(),co(V.id),na(V.start)},children:Z+1},V.id)):null]}),t.jsx("p",{className:"dub-maker-overlap-note",children:"Lines may overlap when two characters speak at the same time."})]}),t.jsxs("div",{className:`dub-maker-status ${ke.tone}`,role:"status",children:[ke.tone==="success"?t.jsx(nt,{size:17}):t.jsx(qC,{size:17}),t.jsx("span",{children:ke.text})]})]}),t.jsxs("aside",{className:"dub-maker-lines panel",children:[t.jsxs("div",{className:"dub-maker-lines-header",children:[t.jsxs("div",{children:[t.jsx("span",{children:"3 · LINES"}),t.jsxs("strong",{children:[ct.length," marked"]})]}),t.jsx("button",{className:"ghost-button icon-button",disabled:!b||ce,title:"Add line at playhead",type:"button",onClick:Sr,children:t.jsx(xy,{size:18})})]}),t.jsx("div",{className:"dub-maker-line-list",children:ct.length?ct.map((V,Z)=>t.jsxs("button",{className:`dub-maker-line-row ${V.id===ze?.id?"active":""}`,type:"button",onClick:()=>co(V.id),children:[t.jsx("b",{children:String(Z+1).padStart(2,"0")}),t.jsxs("span",{children:[t.jsx("strong",{children:V.character||"Character"}),t.jsxs("small",{children:[Fa(V.start)," - ",Fa(V.end)]})]})]},V.id)):t.jsxs("div",{className:"dub-maker-empty-lines",children:[t.jsx(HC,{size:28}),t.jsx("strong",{children:"No lines yet"}),t.jsx("span",{children:"Move the playhead to a spoken line and press Add Line Here."})]})}),ze?t.jsxs("div",{className:"dub-maker-line-editor",children:[t.jsxs("div",{className:"dub-maker-line-editor-head",children:[t.jsx("strong",{children:"Edit selected line"}),t.jsx("button",{className:"ghost-button icon-button danger",title:"Delete line",type:"button",onClick:()=>sa(ze.id),children:t.jsx(Ho,{size:17})})]}),t.jsx(sc,{label:"Character",maxLength:60,value:ze.character,onChange:V=>Io(ze.id,{character:V})}),t.jsxs("label",{className:"dub-maker-field",children:[t.jsxs("span",{children:["Caption ",t.jsx("small",{children:"optional"})]}),t.jsx("textarea",{maxLength:300,rows:3,value:ze.caption,onChange:V=>Io(ze.id,{caption:V.target.value})})]}),t.jsx(sc,{label:"File name",maxLength:64,value:ze.filename,onChange:V=>Io(ze.id,{filename:V})}),t.jsxs("div",{className:"dub-maker-time-grid",children:[t.jsx(Vv,{label:"Start",value:ze.start,onCh
46ange:V=>Io(ze.id,{start:V})}),t.jsx(Vv,{label:"End",value:ze.end,onChange:V=>Io(ze.id,{end:V})})]}),t.jsxs("div",{className:"dub-maker-boundary-actions",children:[t.jsx("button",{className:"ghost-button",disabled:ce,type:"button",onClick:()=>Xi("start"),children:"Set Start"}),t.jsx("button",{className:"ghost-button",disabled:ce,type:"button",onClick:()=>Xi("end"),children:"Set End"}),t.jsxs("button",{className:"ghost-button",disabled:ce,type:"button",onClick:()=>{Vc(ze)},children:[t.jsx(_e,{size:16})," Preview"]})]}),aa.find(V=>V.startsWith(ze.id))?t.jsx("p",{className:"dub-maker-line-error",children:aa.find(V=>V.startsWith(ze.id))?.replace(`${ze.id}: `,"")}):null]}):null]})]})}function Dv({file:e,label:o,onRemove:r,url:i}){return t.jsxs("div",{className:"dub-maker-separated-track",children:[t.jsxs("div",{children:[t.jsx("strong",{children:o}),t.jsx("span",{title:e.name,children:e.name})]}),t.jsx("audio",{controls:!0,preload:"metadata",src:i}),t.jsx("button",{className:"ghost-button icon-button danger",title:`Remove ${o.toLowerCase()} track`,type:"button",onClick:r,children:t.jsx(Tt,{size:16})})]})}function yE(e){if(e.phase==="downloading-model"){const o=Lv(e.loadedBytes),r=Lv(e.totalBytes);return r?`Downloading separation model · ${o}/${r} MB`:`Downloading separation model · ${o} MB`}return e.phase==="loading-model"?"Loading separation model":e.phase==="separating"?e.currentChunk&&e.totalChunks?`Separating audio · ${e.currentChunk}/${e.totalChunks}`:"Separating audio":e.phase==="encoding"?"Preparing previews":"Preparing audio"}function Lv(e){return!e||!Number.isFinite(e)?"0":(e/(1024*1024)).toFixed(e>=100*1024*1024?0:1)}async function vE(e,o,r){const i=Math.max(1,Math.ceil(e.duration*o)),c=new Float32Array(i),l=new Float32Array(i),p=e.getChannelData(0),f=e.numberOfChannels>1?e.getChannelData(1):p,b=e.sampleRate/o,y=262144;for(let w=0;w<i;w+=y){const M=Math.min(i,w+y);for(let E=w;E<M;E+=1){const z=Math.min(p.length-1,E*b),N=Math.floor(z),j=Math.min(p.length-1,N+1),D=z-N;c[E]=(p[N]??0)*(1-D)+(p[j]??0)*D,l[E]=(f[N]??0)*(1-D)+(f[j]??0)*D}r?.(M/i),await SE()}return{left:c,right:l,sampleRate:o}}async function kE(e){const o=window.AudioContext??window.webkitAudioContext;if(!o)throw new Error("Audio import is not supported in this browser.");const r=new o;try{return(await r.decodeAudioData(await e.arrayBuffer())).duration}finally{await r.close().catch(()=>{})}}function wE(e,o){return new Promise((r,i)=>{let c=0;const l=()=>{window.clearTimeout(c),e.removeEventListener("seeked",p),e.removeEventListener("error",f)},p=()=>{l(),r()},f=()=>{l(),i(new Error("Video seek failed."))};e.addEventListener("seeked",p,{once:!0}),e.addEventListener("error",f,{once:!0}),c=window.setTimeout(()=>{l(),i(new Error("Video seek timed out."))},6e3),e.currentTime=Math.max(0,Math.min(o,e.duration||o))})}function SE(){return new Promise(e=>window.requestAnimationFrame(()=>e()))}function ic({accept:e,icon:o,label:r,onChange:i}){return t.jsxs("label",{className:"dub-maker-file-field",children:[o,t.jsx("span",{children:r}),t.jsx("input",{accept:e,hidden:!0,type:"file",onChange:c=>{i(c.target.files?.[0]),c.currentTarget.value=""}})]})}function sc({label:e,maxLength:o,onChange:r,optional:i=!1,placeholder:c,value:l}){return t.jsxs("label",{className:"dub-maker-field",children:[t.jsxs("span",{children:[e," ",i?t.jsx("small",{children:"optional"}):null]}),t.jsx("input",{maxLength:o,placeholder:c,value:l,onChange:p=>r(p.target.value)})]})}function Vv({label:e,onChange:o,value:r}){return t.jsxs("label",{className:"dub-maker-field",children:[t.jsx("span",{children:e}),t.jsx("input",{min:"0",step:"0.01",type:"number",value:r,onChange:i=>o(Number(i.target.value))})]})}function xE(e,o,r){const i=Math.max(0,Math.floor(o*e.sampleRate)),c=Math.min(e.length,Math.ceil(r*e.sampleRate)),l=Math.max(1,c-i),p=new Float32Array(l);for(let f=0;f<e.numberOfChannels;f+=1){const b=e.getChannelData(f);for(let y=0;y<l;y+=1)p[y]+=(b[i+y]??0)/e.numberOfChannels}return{samples:p,sampleRate:e.sampleRate}}function RE(e,o){return e.flatMap((r,i)=>!Number.isFinite(r.start)||!Number.isFinite(r.end)?[`${r.id}: Line ${i+1}
46 has an invalid time.`]:r.start<0||r.end<=r.start?[`${r.id}: Line ${i+1} must end after it starts.`]:o>0&&r.end>o+.05?[`${r.id}: Line ${i+1} ends after the source video.`]:r.end-r.start>Iv?[`${r.id}: Line ${i+1} is longer than ${Iv} seconds.`]:r.character.trim()?[]:[`${r.id}: Line ${i+1} needs a character name.`])}function Nm(e,o){return o?Math.max(0,Math.min(100,e/o*100)):0}function Dm(e){return Math.round(e*1e3)/1e3}function Fa(e){if(!Number.isFinite(e)||e<0)return"0:00.00";const o=Math.floor(e/60),r=e-o*60;return`${o}:${r.toFixed(2).padStart(5,"0")}`}function MS(e){return e.split(".").at(-1)?.toLowerCase()??""}function zv(e,o){const r=MS(e.name);return/^[a-z0-9]{2,5}$/.test(r)?r:o}function Ud(e){return e.trim().replace(/\.[^.]+$/,"").replace(/[^a-zA-Z0-9_-]+/g,"_").replace(/^_+|_+$/g,"").slice(0,64)||"line"}function Ov(e){return Ud(e).replace(/_/g,"-").toLowerCase()||"voxalike-dub-mod"}function cc(e){return JSON.stringify(e)}function Uv(e,o){const r=URL.createObjectURL(e),i=document.createElement("a");i.href=r,i.download=o,document.body.append(i),i.click(),i.remove(),window.setTimeout(()=>URL.revokeObjectURL(r),1e3)}const Lm={name:"Voxalike"},PE="score_v1_5",CE={duration:.28,timing:.34,energy:.3,pitch:.08},ME={duration:.3,timing:.36,energy:.3,pitch:.04},jE=60;function TE(e,o,r={}){if(e=Fv(e),o=Fv(o),Wv(o),r.localReference&&Wv(e),LE(o)){const M=["Score Judging","No clear voice detected"];return r.localReference&&M.push("Imported dub mod reference"),{duration:0,timing:0,energy:0,pitch:0,total:0,rating:Hv(0),notes:M}}const i=EE(e,o),c=AE(e,o),l=IE(e,o),p=NE(e,o),f=DE(e),b=i*f.duration+c*f.timing+l*f.energy+p*f.pitch,y=dc(OE(b)),w=["Score Judging"];return r.localReference&&w.push("Imported dub mod reference"),(e.pitchConfidence<.3||e.pitchContour.filter(Boolean).length<3)&&w.push("Pitch weight reduced for this clip"),{duration:dc(i),timing:dc(c),energy:dc(l),pitch:dc(p),total:y,rating:Hv(y),notes:w}}function Fv(e){return{...e,rms:Bv(e.rms),peak:Bv(e.peak)}}function Bv(e){return e===void 0||!Number.isFinite(e)||e<0?e:Math.min(1,e)}function Wv(e){if(!Number.isFinite(e.duration)||e.duration<=0||e.duration>jE)throw new Error("Invalid duration feature");if(!Number.isFinite(e.voicedRatio)||e.voicedRatio<0||e.voicedRatio>1)throw new Error("Invalid voiced ratio feature");if(e.rms!==void 0&&(!Number.isFinite(e.rms)||e.rms<0||e.rms>1))throw new Error("Invalid RMS feature");if(e.peak!==void 0&&(!Number.isFinite(e.peak)||e.peak<0||e.peak>1))throw new Error("Invalid peak feature");lc(e.energyEnvelope,256,"energy envelope"),lc(e.pitchContour,256,"pitch contour"),lc(e.pausePositions,64,"pause positions"),lc(e.onsetTimes,64,"onset times"),lc(e.peakTimes,64,"peak times")}function EE(e,o){const r=Math.abs(e.voicedDuration-o.voicedDuration),i=Math.max(.18,e.voicedDuration*.48);return 100*(1-Math.min(1,r/i))}function AE(e,o){const r=Vm(e.onsetTimes,o.onsetTimes,e.duration),i=Vm(e.peakTimes,o.peakTimes,e.duration),c=Vm(e.pausePositions,o.pausePositions,e.duration);return r*.42+i*.38+c*.2}function IE(e,o){const r=iu(e.energyEnvelope,64),i=iu(o.energyEnvelope,64);let c=0;for(let p=0;p<r.length;p+=1)c+=Math.abs(r[p]-i[p]);const l=c/Math.max(1,r.length);return 100*(1-Math.min(1,l/.62))}function NE(e,o){const r=iu(e.pitchContour,48),i=iu(o.pitchContour,48),c=Gv(r),l=Gv(i);if(!c||!l)return 55;const p=[];let f=0;for(let E=0;E<r.length;E+=1)if(r[E]>0&&(f+=1),r[E]>0&&i[E]>0){const z=1200*Math.log2(r[E]/c),N=1200*Math.log2(i[E]/l);p.push(Math.abs(N-z))}if(p.length<3)return 55;const b=p.reduce((E,z)=>E+z,0)/p.length,y=100*(1-Math.min(1,b/1600)),w=100*Math.min(1,p.length/Math.max(1,f)),M=y*.78+w*.22;return Math.max(42,M)}function DE(e){return e.pitchConfidence>=.3&&e.pitchContour.filter(Boolean).length>=3?CE:ME}function Vm(e,o,r){if(e.length===0&&o.length===0)return 100;if(e.length===0||o.length===0)return 35;const i=Math.max(.12,r*.12),c=e.map(f=>{const b=Math.min(...o.map(y=>
46Math.abs(y-f)));return Math.min(1,b/i)}),l=Math.min(1,Math.abs(e.length-o.length)/Math.max(e.length,1)),p=c.reduce((f,b)=>f+b,0)/c.length;return 100*(1-Math.min(1,p*.82+l*.18))}function LE(e){if(VE(e))return!0;const o=e.pitchContour.filter(b=>b>0),r=o.length/Math.max(1,e.pitchContour.length);if(e.pitchConfidence>=.12&&o.length>=3&&r>=.04)return!1;const c=qv(e.energyEnvelope,.9)-qv(e.energyEnvelope,.1),l=zE(e.energyEnvelope),p=e.onsetTimes.length/Math.max(.4,e.duration);return!(e.onsetTimes.length>=1&&p<=8&&c>=.18&&l>=.06&&e.voicedDuration>=.18)}function VE(e){if(typeof e.rms!="number"||typeof e.peak!="number")return!1;const o=e.rms<$v(-45),r=e.peak<$v(-32),i=e.pitchConfidence>=.12&&e.pitchContour.filter(l=>l>0).length>=3,c=e.voicedDuration>=.22&&e.voicedRatio>=.18;return o&&r&&!i&&!c}function $v(e){return 10**(e/20)}function iu(e,o){return e.length===0?Array.from({length:o},()=>0):e.length===1?Array.from({length:o},()=>e[0]):Array.from({length:o},(r,i)=>{const c=i/Math.max(1,o-1)*(e.length-1),l=Math.floor(c),p=Math.min(e.length-1,l+1),f=c-l;return e[l]*(1-f)+e[p]*f})}function Gv(e){const o=e.filter(i=>i>0).sort((i,c)=>i-c);if(!o.length)return;const r=Math.floor(o.length/2);return o.length%2===1?o[r]:(o[r-1]+o[r])/2}function qv(e,o){if(e.length===0)return 0;const r=[...e].sort((c,l)=>c-l),i=Math.min(r.length-1,Math.max(0,Math.round((r.length-1)*o)));return r[i]}function zE(e){if(e.length===0)return 0;const o=e.reduce((i,c)=>i+c,0)/e.length,r=e.reduce((i,c)=>i+(c-o)**2,0)/e.length;return Math.sqrt(r)}function lc(e,o,r){if(!Array.isArray(e)||e.length>o||e.some(i=>!Number.isFinite(i)))throw new Error(`Invalid ${r}`)}function dc(e){return Math.round(Math.max(0,Math.min(100,e)))}function OE(e){if(e<=35)return e;const r=35+65*Math.max(0,Math.min(1,(e-35)/65))**.58;if(r<=60)return r;const c=9*Math.min(1,(r-60)/40)**1.25;return UE(r+c)}function UE(e){return e<=80?e:e<88?80+(e-80)*(15/8):95+(e-88)*5}function Hv(e){return e>=100?"Perfect Dub":e>=95?"Viral Performance":e>=85?"Scene Stealer":e>=75?"Great Take":e>=60?"Solid Dub":e>=40?"Getting There":"Keep Practicing"}const FE=1800*1e3,BE=1800*1e3;function WE(e=Date.now()){return{sessionId:crypto.randomUUID(),expiresAt:new Date(e+FE).toISOString()}}function $E(e,o=Date.now()){const r=crypto.randomUUID();return{roundId:r,roundToken:`local:${r}`,clip:{title:e.clip.title,caption:e.clip.caption,duration:e.clip.duration,sourceLabel:"Local Pack / Unverified Reference"},expiresAt:new Date(o+BE).toISOString()}}function GE(e,o){return TE(e,o,{localReference:!0})}function qE(e,o){if(!e||e.roomId!==o.roomId)return[];const r=new Map(o.players.map(c=>[c.id,c])),i=[];for(const c of e.players){if(c.id===o.playerId)continue;const l=r.get(c.id);if(!l){i.push({id:`presence:${o.roomId}:${c.id}:left`,kind:"left",playerId:c.id,playerName:c.name});continue}!c.reconnectDeadlineAt&&l.reconnectDeadlineAt?i.push({id:`presence:${o.roomId}:${l.id}:disconnected:${l.reconnectDeadlineAt}`,kind:"disconnected",playerId:l.id,playerName:l.name}):c.reconnectDeadlineAt&&!l.reconnectDeadlineAt&&i.push({id:`presence:${o.roomId}:${l.id}:reconnected:${l.lastSeenAt}`,kind:"reconnected",playerId:l.id,playerName:l.name})}return i}function zm(e){return e?.players.filter(o=>!o.reconnectDeadlineAt).length??0}const qi={title:"Voxalike Guide",seoTitle:"Voxalike Guide - How to Play and Make Mods",description:"Learn how to play Voxalike and make mods.",sections:[{id:"how-to-play",kicker:"HOW TO PLAY",title:"Choose a Dub Pack and make the scene yours.",body:"A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.",steps:["Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.","Open PLAY and choose Single Player or Multiplayer.","For Single Player, select one of your installed Dub Packs and start the pack.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio."],images:[{src:"/guide/manage-dub-packs.png",alt:"
46Voxalike main menu with the Mods button below Single Player and Multiplayer",title:"Where do I manage my Dub Packs?",caption:"Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs."}]},{id:"make-dub-mods",kicker:"MAKE A DUB MOD",title:"How to Make a Dub Pack",body:"First, open the Mod Maker and drag an MP4 file into it, as shown below.",links:[{label:"Dub Mod Maker",href:"https://app.voxalike-mod-maker.workers.dev/",prefix:"This is the Dub Mod Maker Tool â",note:"Created by Brunoxx."},{label:"Technical documentation",href:"/docs/dub-pack-format",prefix:"For the Dub Pack structure and technical details â"}],steps:["Drag an MP4 file into the Mod Maker.","Click Split voice & background to separate the voice and background audio.","Use Start to split the video into clips, then use End to mark the end of the final clip.","Under Lines, add the corresponding character and dialogue to each clip.","Complete the basic mod information under Mod setup.","Open Validation & export, check whether any information is missing, and export the Dub Pack."],content:[{type:"image",image:{src:"/guide/mod-maker-import-mp4.png",alt:"Mod Maker Media and timeline page showing where to drag an MP4 file",caption:"Drag an MP4 file into the Mod Maker."}},{type:"paragraph",text:"Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip."},{type:"heading",text:"How to split the video into clips"},{type:"paragraph",text:"The Start button is used to place markers. The first marker becomes the beginning of the first clip."},{type:"paragraph",text:"To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines."},{type:"paragraph",text:"The End button places a marker at the end of the video. This marker becomes the end of the final clip."},{type:"heading",text:"How to assign characters to clips"},{type:"paragraph",text:"Under Characters, enter the names of all characters used in the Dub Pack."},{type:"paragraph",text:"Then go to Lines and select the corresponding character for each clip."},{type:"heading",text:"How to separate the voice and background audio"},{type:"paragraph",text:"Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video."},{type:"image",image:{src:"/guide/mod-maker-split-and-mark.png",alt:"Mod Maker timeline showing Split voice and background, Start and End buttons",caption:"Use Split voice & background to separate the audio. Use Start and End to mark the clips."}},{type:"heading",text:"How to set the mod title and author"},{type:"paragraph",text:"Enter the mod title, author and other information under Mod setup."},{type:"heading",text:"The complete process"},{type:"paragraph",text:"Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip."},{type:"paragraph",text:"Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup."},{type:"paragraph",text:"Finally, open Validation & export, check whether any information is missing, and export the Dub Pack."}]},{id:"upload-mod-to-workshop",kicker:"WORKSHOP UPLOAD",title:"How to Upload a Mod to Workshop",body:"On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.",steps:["On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.","Fill in the title, description and tags, then choose the language spoken in the mod.","Confirm that you have the rights or permission to share the content, then select Publish Mod.","After publishing, open My Mods if you need to edit the listing or replace the ZIP."],content:[{type:"image",image:{src:"/guide/creator-workshop-upload-mod.png",alt:"Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields",caption:"Upload Mod in Creator Workshop."}},{type:"heading",text:"Upload the ZIP"}
46,{type:"paragraph",text:"Choose Dub Mod or Judge Mod, then select the mod ZIP from your device."},{type:"heading",text:"Complete the Workshop listing"},{type:"paragraph",text:"Fill in the title, description and tags, then choose the language spoken in the mod."},{type:"heading",text:"Choose the correct language"},{type:"paragraph",text:"Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it."},{type:"paragraph",text:"If you selected the wrong language, open My Mods and edit the mod information."},{type:"heading",text:"Publish the mod"},{type:"paragraph",text:"Confirm that you have the rights or permission to share the content, then select Publish Mod."},{type:"heading",text:"Edit the mod later"},{type:"paragraph",text:"After publishing, open My Mods if you need to edit the listing or replace the ZIP."}]},{id:"dub-pack-format",kicker:"TECHNICAL DOCUMENTATION",title:"Dub Pack Format Reference",body:"A Dub Pack uses one scene video and line-by-line audio. Pack metadata is stored in _pack_info.ini, while each line can use an .ini file for its caption, timestamp and characters.",code:`My Dub Pack/ 47 _pack_info.ini 48 _icon.png 49 dub_video.mp4 50 _backing_track.mp3 51 01_opening.mp3 52 01_opening.ini 53 02_reply.wav 54 02_reply.txt 55 56_pack_info.ini 57[data] 58title="My Dub Pack" 59authors=["Creator Name"] 60language="English" 61tags="movie,challenge" 62 6301_opening.ini 64[data] 65title="Opening Line" 66caption="Are you for real?" 67dub_timestamps=[17.700] 68dub_characters=["Character Name"] 69 70# When characters speak together, list every speaker: 71dub_characters=["Character One", "Character Two"]`,cards:[{title:"Audio",body:"Required for each line: .wav, .mp3 or .ogg"},{title:"Video",body:"Use dub_video.mp4 or dub_video.webm for the full scene"},{title:"Timing",body:"Use dub_timestamps in each line .ini file"},{title:"Backing Track",body:"Optional: _backing_track.mp3, .wav or .ogg"},{title:"Caption",body:"Use a same-name .txt file for plain text or an .ini file for caption and metadata"},{title:"Characters",body:"Use dub_characters for the speaker. List multiple names when characters speak together"},{title:"Cover",body:"Optional: _icon, cover, poster, thumbnail or preview image"},{title:"Line Image",body:"Optional image for an individual line; the video is used first when present"}]},{id:"judge-pack-format",kicker:"JUDGE PACK",title:"Judge Pack Format Reference",body:"Judge Mods are separate from Dub Mods. They change the score reveal presentation only: the four judges, their names, labels, feedback lines and short reaction sounds. They do not change scoring rules, gameplay, HTML, CSS or JavaScript.",code:`Simple Judge Mod/ 72 judge_pack.json 73 assets/duration.webp 74 assets/timing.webp 75 assets/energy.webp 76 assets/pitch.webp 77 78Simple judge_pack.json 79{ 80 "schemaVersion": 1, 81 "id": "simple-judges", 82 "title": "Simple Judges", 83 "author": "Creator Name", 84 "version": "1.0.0", 85 "judges": { 86 "duration": "assets/duration.webp", 87 "timing": "assets/timing.webp", 88 "energy": "assets/energy.webp", 89 "pitch": "assets/pitch.webp" 90 } 91} 92 93Single-sound judge_pack.json 94{ 95 "schemaVersion": 1, 96 "id": "simple-judges-with-sound", 97 "title": "Simple Judges With Sound", 98 "author": "Creator Name", 99 "version": "1.0.0", 100 "judges": { 101 "duration": { 102 "image": "assets/duration.webp", 103 "sounds": "assets/reveal.mp3" 104 }, 105 "timing": "assets/timing.webp", 106 "energy": "assets/energy.webp", 107 "pitch": "assets/pitch.webp" 108 } 109} 110 111Advanced Judge Mod/ 112 judge_pack.json 113 assets/duration.webp 114 assets/timing.webp 115 assets/energy.webp 116 assets/pitch.webp 117 assets/reveal.mp3 118 assets/low.mp3 119 assets/mid.mp3 120 assets/high.mp3 121 assets/elite.mp3 122 123Advanced judge_pack.json 124{ 125 "schemaVersion": 1, 126 "id": "my-funny-judges", 127 "title": "My Funny Judges", 128 "author": "Creator Name", 129 "version": "1.0.0", 130 "judges": { 131 "duration": { 132 "image": "assets/duration.webp", 133 "name": "Peter", 134 "label": "Duration", 135 "feedback": { 136 "low": "That was painfully short.", 137 "mid": "You survived the line.", 138 "high": "Clean delivery.", 139 "elite": "That timing was illegal." 140 }, 141 "sounds": { 142 "reveal": "assets/reveal.mp3", 143 "low": "assets/low.mp3", 144 "mid": "assets/mid.mp3", 145 "high": "assets/high.mp3", 146 "elite": "assets/elite.mp3" 147 } 148 }, 149 "timing": "assets/timing.webp", 150 "energy": "assets/energy.webp", 151 "pitch": "assets/pitch.webp" 152 } 153}
153`,cards:[{title:"Manifest",body:"Required: judge_pack.json in the ZIP root with schemaVersion 1"},{title:"Judge Slots",body:"Simple format can map duration, timing, energy and pitch directly to image paths"},{title:"Images",body:"Allowed: PNG, JPG, JPEG or WebP. Recommended: transparent 512x768 or larger"},{title:"Single Sound",body:"Set sounds to one audio path when you only want a single reveal reaction"},{title:"Score-Tier Sounds",body:"Use reveal plus low, mid, high and elite when you want different reactions by score tier"},{title:"Feedback",body:"Optional text tiers: low, mid, high and elite"},{title:"Safe Package",body:"No HTML, JavaScript, CSS, SVG, fonts, external URLs or absolute paths"}]},{id:"install-workshop-mods",kicker:"WORKSHOP MODS",title:"How to subscribe to and install Workshop mods.",body:"Workshop subscriptions belong to your Voxalike account, while playable installations are stored in the browser you are using. Follow this guide to add a Dub Mod, understand Subscribe, Download and Import, and make sure every player has the correct mod before a game starts.",steps:["Sign in to Voxalike, open Workshop and choose a Dub Mod you want to play.","Select Subscribe. This saves the mod to your account so Voxalike can keep it in your subscribed library.","Return to the PLAY home screen and open Mods. Voxalike will sync subscribed mods and install any missing or updated package in this browser.","Keep the Mods page open until the installation progress finishes and the mod appears as Installed.","Open Single Player to choose the installed mod, or create a Multiplayer room and let the host select it.","When a Workshop mod is selected in Multiplayer, signed-in guests can install the matching room version automatically before they Ready Up.","Repeat the installation on another browser or device. Your subscription follows your account, but the playable files are stored separately in each browser."],images:[{src:"/guide/manage-dub-packs.png",alt:"Voxalike PLAY home screen showing the Mods button used to manage subscribed Dub Packs",title:"Open Mods after subscribing",caption:"The Mods screen synchronizes your subscriptions, installs missing packages and shows the Dub Mods and Judge Mods saved in this browser."}],faqGroups:[{title:"Subscribe, Download and Import",items:[{question:"What does Subscribe do?",paragraphs:["Subscribe connects a Workshop mod to your signed-in account. The next time you open Mods, Voxalike checks the subscribed package and installs it or replaces an older local revision.","A subscription is not the same as a permanent cloud installation. The playable video, audio and metadata are stored in the current browser, so a different device must install its own copy."]},{question:"When should I use Download or Import Mod?",paragraphs:["Download gives you the Workshop ZIP as a normal file. It is useful when you are not signed in, want a local backup, or need to move the package manually.","Import Mod adds a ZIP or folder from your device to Voxalike. Imported packages are local mods: they are not automatically linked to a Workshop subscription and are not uploaded to other players."]}]},{title:"Using mods in Multiplayer",items:[{question:"Why does a room say Installing room mod?",paragraphs:["The host selected a Workshop mod that your browser does not have yet, or your installed copy is an older revision. Wait for the installation to finish before pressing Ready Up.","If the host selected a local ZIP instead, every player must import the same package themselves. A private file on the host's computer cannot be copied through the room automatically."],links:[{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Why does Voxalike report the wrong mod version?",paragraphs:["The room compares package identity and clip information so players do not dub different versions of the same scene. Open Mods, allow the subscribed package to update, and rejoin or Ready Up again.","For local packages, remove the mismatched copy and import the exact ZIP used by the host. A matching title alone is not enough."]}]},{title:"Storage and updates",items:[{question:"Why is a subscribed mod missing on a new device?",paragraphs:["Sign in with the same Voxalike account, open Mods and wait for subscription sync. Browser storage is device-specific, so the new browser still needs to download the package once.","Private browsing, aggressive storage cleanup and limited free space can prevent large packages from remaining installed. Use a normal browser profile and keep enough free device storage for the ZIP and extracted media."]},{question:"How do Workshop mod updates reach subscribers?",paragraphs:["When a creator replaces a Workshop ZIP, Voxalike keeps the listing and subscription but gives the package a new revision. Opening Mods checks for that revision and installs the update.","If a listing is hidden or permanently removed, it is no longer available as an active subscription. Locally imported files remain under the control of the person who downloaded them."]}]},{title:"Installation troubleshooting",items:[{question:"What should I do if installation stops or fails?",paragraphs:["Keep the page open, check the network connection and try the install action again from Mods. Very large Dub Mods can take time to download, validate and store even after the progress bar begins moving.","If the same mod repeatedly fails, include the mod name, browser, device, available storage and the exact error message in a bug report. Testing a smaller Workshop mod helps determine whether the problem is package-specific."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]}]}]},{id:"multiplayer",kicker:"MULTIPLAYER",title:"How to play Voxalike Multiplayer.",body:"Voxalike Multiplayer uses private room codes and a host-controlled Dub Mod. Learn how Co-op and PVP differ, how Round Robin and Claim Mode assign lines, why players may need to install a room mod, and how to reconnect without losing the current run.",steps:["Open Multiplayer and either create a room or enter a room code from the host.","The host chooses Co-op or PVP, selects an installed Dub Mod and configures the available room options.","In Co-op, choose Round Robin for an ordered turn sequence or Claim Mode so players decide who records each open line.","Wait while Voxalike installs the selected Workshop mod for guests who do not have the matching revision.","Each player selects Ready Up. The host starts only after the room and mod are ready.","Record the assigned or claimed lines. Use replay, retry and the available host recovery tools when a line needs attention.","Finish the Dub Mod to combine the room's takes, view the result and save the completed dub."],faqGroups:[{title:"Cho
153ose a game mode",items:[{question:"What is the difference between Co-op and PVP?",paragraphs:["Co-op builds one shared dub. Players perform different lines and the finished video combines the room's saved takes. Co-op rooms currently support up to 12 players.","PVP gives players comparable performances and scores within the competitive room flow. PVP currently supports up to 8 players and may require a smaller Dub Mod when player and clip counts would create too much browser work."]},{question:"What are Turn Battle and Parallel Race?",paragraphs:["Turn Battle is the original PVP format: every player records the current clip in order, then the room moves to the next clip.","Parallel Race starts everyone together. Each player independently records the full same clip list and may re-record before submitting when re-records are enabled. Quality is the baseline score; speed can fill up to half of the remaining distance to 100, and only strong-quality performances receive the full benefit. Low-quality performances receive little or no speed reward, finishes within 2% of the fastest time share the fastest rating, and 100 quality always remains 100. Results show only each player's final score and completion time."]},{question:"What is Round Robin?",paragraphs:["Round Robin assigns turns in a sequence. The room shows whose turn it is, and the next assignment becomes available after the current take is saved or skipped.","The host can optionally randomize player order. Round Robin works well when everyone wants a predictable number of turns without deciding who takes every line."]},{question:"What is Claim Mode?",paragraphs:["Claim Mode leaves each Co-op line open until a player claims it. The group can decide who suits a character or line instead of following a fixed turn order.","A player can release a line before saving, and the host can recover a stuck claim or redo the previous line through Host Tools when the action is available. Claim Mode does not automatically reserve every line for one character."]}]},{title:"Room setup and Ready Up",items:[{question:"Who controls the room?",paragraphs:["The host selects the mode, Dub Mod and room options, then starts the game. Guests can prepare their microphone, wait for the selected package and mark themselves ready.","If a player cannot Ready Up, first check whether the room mod is still installing or whether the local package does not match the host's version."],links:[{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Can a room use a local ZIP?",paragraphs:["Yes, but every participant must import the exact same local package. Workshop mods are easier for most rooms because Voxalike can locate and install the host's selected revision for signed-in guests."]},{question:"Can we hear the original line while recording?",paragraphs:["Solo and Co-op can optionally play the original line quietly as a timing guide while recording. Use it when a long sentence is difficult to follow, but wear headphones so the guide audio does not leak back into the microphone."]}]},{title:"Disconnects and Host Tools",items:[{question:"What happens if I refresh or briefly disconnect?",paragraphs:["Voxalike stores the active room code and player token in the browser and attempts to reconnect when the page becomes visible or the network returns. Use the same browser profile so the saved room identity is still available.","Locally saved takes can also be restored alongside current room state. Recovery data and rooms are temporary, so reconnect promptly instead of treating the room as permanent storage."]},{question:"What can the host do when a line is stuck?",paragraphs:["Host Tools contains recovery actions appropriate to the current Co-op state. Depending on the assignment, the host can release a stuck claim, redo the last line or skip a line that cannot be played while preserving the original audio in the finished dub.","Use recovery actions only after confirming the affected player is not still recording or uploading a take."]},{question:"How do we avoid losing the finished result?",paragraphs:["After the last line, wait for the result and local recording copy to finish saving. Recent Dubs keeps up to five recent multiplayer results available from the Create or Join screen in the same browser for up to seven days.","Every player who wants a copy should still download it promptly because mobile browsers can remove local site storage when space is low."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]}]}]},{id:"recover-recent-dubs",kicker:"RECENT DUBS",title:"How to recover and download recent multiplayer dubs.",body:"Completed multiplayer recordings are valuable and browser video export can take time. Voxalike Recent Dubs gives players another path back to up to five completed room results so they can reopen the result and download the video or audio after returning to the lobby.",steps:["Finis
153h every assigned line and wait for Voxalike to mark the multiplayer run complete.","Return to the Multiplayer Create or Join screen in the same browser profile.","Find Recent Dubs or Past Results and choose View & Download for the completed Dub Mod.","If Voxalike asks for the Dub Mod, open Mods and install the exact Workshop revision used by the room.","Reopen the restored result and choose the landscape video, Shorts video or audio download you need.","Keep Voxalike visible while the browser prepares the video, and wait until the file download has started and completed.","Save important files promptly. A complete local copy can remain in Recent Dubs for up to seven days, but it is not permanent cloud video storage."],faqGroups:[{title:"What Recent Dubs keeps",items:[{question:"How many results are available and for how long?",paragraphs:["Voxalike shows up to five recent completed multiplayer dubs saved in the browser. Server recovery remains limited to the two newest results for approximately two hours. When the browser finishes saving a complete local recording copy, the same device can reopen it for up to seven days.","The local copy stays in that browser profile and uses device storage. It is not a public gallery and does not replace downloading the finished video."]},{question:"Does returning to the lobby immediately erase a completed dub?",paragraphs:["No. Once the run has completed and the result has been captured, returning to the lobby should place it in Recent Dubs. Open it from the Multiplayer Create or Join screen instead of starting the entire performance again.","A run that never completed, expired before its takes finished saving, or lost its browser recovery data may not produce a recoverable recent result."]},{question:"Can I open a recent dub on another computer?",paragraphs:["Do not rely on that. Recent-result references are kept in the participating browser and can also arrive through the active room state. Use the same browser profile that played the room, without clearing site data.","Download the final file before changing devices, clearing browser storage or using a private window."]}]},{title:"Reopening and exporting",items:[{question:"Why does the restored result need the original Dub Mod?",paragraphs:["The room saves recorded takes and assignment data, while the installed Dub Mod provides the scene video, original audio, captions and timing needed to reconstruct the finished dub.","Install the exact Workshop mod from Mods, then try View & Download again. A different package with the same title may not match the recorded assignments."],links:[{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Why must the page remain visible during video preparation?",paragraphs:["Voxalike renders the video locally with browser media and canvas features. Browsers can pause or heavily slow that work when the tab is hidden, the device sleeps or another demanding program uses the available memory.","Keep the tab visible, leave the computer awake and close unnecessary heavy applications. If preparation is interrupted, use the retry action after returning to Voxalike."]},{question:"Why is the downloaded video WebM?",paragraphs:["WebM is the most reliable format for Voxalike's browser-side export pipeline. Current Chrome and Edge can play it directly; other players or editing tools may require conversion to MP4.","The audio download is useful when a phone or local media player cannot handle the video file immediately."]}]},{title:"If the result is missing",items:[{question:"What information should I send for recovery help?",paragraphs:["Send the room code, approximate play time with time zone, host username, your player name, Dub Mod name, browser and whether the run reached the completed result. Do not clear site data while the result is being investigated.","Recovery is not guaranteed after temporary room data expires, so report the problem as soon as possible."],links:[{label:"How to Play",href:"/guide/how-to-play"},{label:"Open the bug-report checklist",href:"/guide/faq"}]}]}]},{id:"microphone-troubleshooting",kicker:"MICROPHONE HELP",title:"How to troubleshoot microphone and recording problems.",body:"Voxalike records through the browser, so permission, the selected input, operating-system privacy controls and mobile audio focus all affect a take. Use this checklist when Record does nothing, Mic Check works but a round is silent, a mobile microphone stops after switching apps, or the game reports an interrupted take.",steps:["Use a current browser on the secure https://voxalike.com address and open Voxalike in a normal browser profile.","Open Settings, choose the intended microphone and run Mic Check before entering a game.","Allow microphone access in both the browser's site permissions and the operating system's app permissions.","During calibration, stay quiet for the noise sample, then speak at your normal playing volume and apply the result.","Close other tabs, voice-chat apps or recording programs that may have exclusive control of the microphone.","If a take is interrupted, return to Voxalike, use Try Again and keep the game visible while recording.","If the problem continues, copy the Voxalike diagnostic and send it with the device, browser, mode, mod name and a screenshot or short recording."],faqGroups:[{title:"Permission and input checks",items:[{question:"Record does nothing or permission is denied. What should I check?",paragraphs:["Select the icon beside the website address and set Microphone to Allow for voxalike.com. Then open Voxalike Settings, confirm the correct input device and run Mic Check again.","On Windows or macOS, also confirm that the operating system allows the browser to use the microphone. On Android or iOS, confirm the browser app has microphone permission. Restart the browser after changing system permissions."]},{question:"Why is the wrong microphone being used?",paragraphs:["A headset connection, USB device change or Bluetooth reconnect can change the browser's default input. Choose the device again in Voxalike Settings and apply a new Mic Check.","Disconnect unused microphones when device names are ambiguous. Bluetooth headset mode may also sound different from the same headset used only for music playback."]}]},{title:"Mic Check works but the game does not",items:[{question:"Why can Mic Check hear me but a round records silence?",paragraphs:["The browser may keep a live-looking microphone track while delivering digital silence after audio focus changes, a device reconnect or an interrupted mobile session. Voxalike checks recorded levels and should stop the take instead of saving unusable audio.","Fully close other apps using the microphone, return to Voxalike, select the input again and retry. On a phone, avoid switching apps or locking the screen during a line."]},{question:"Why does the microphone stop after switching tabs or apps?",paragraphs:["Browsers may suspend audio processing or give microphone ownership to another app while Voxalike is in the background. When you return, wait for the page to become active and use the retry flow rather than repeatedly pressing Record.","If the page itself reloads, use the available single-player draft recovery or multiplayer reconnect option. Browser memory savers and tab-suspension extensions can make background reloads more likely."],links:[{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Why was my scream quieter or a soft line louder?",paragraphs:["Voice cleanup reduces steady noise and balances recordings made with different microphones. A poor noise sample or unusually different speaking distance can make intentional dynamics less accurate.","Calibrate in a quiet room, keep the same microphone distance while playing and avoid automatic microphone enhancement in multiple apps at the same time."]}]},{title:"Browser-specific symptoms",items:[{question:"Why does iPhone or iPad fail after recording a take?",paragraphs:["Safari-based browsers can occasionally produce microphone audio that their own media decoder cannot immediately reopen. Current Voxalike versions include a fallback for supported iOS recordings and show a take-processing diagnostic when decoding still fails.","Keep iOS and the browser current, try Safari if another iPhone browser fails, and send the complete diagnostic rather than only the visible error title."]},{question:"Why does Mic Check show a black screen or behave differently in Incognito?",paragraphs:["If Voxalike works in a private window but fails in the normal profile, a browser extension, injected overlay, cached script or strict site setting is likely interfer
153ing with the page.","Disable extensions for voxalike.com one at a time, especially video, ad, privacy and page-modification tools. Then reload the normal profile and test Mic Check again."]},{question:"Can a VPN, proxy or security filter affect recording?",paragraphs:["Yes. Browser microphone APIs require a trustworthy secure page. A VPN, proxy, antivirus filter or network interception tool that changes HTTPS behavior can cause permission or media-device failures even when the address begins with https.","Temporarily test without the filter or on a different trusted network. Never bypass a security warning merely to enable microphone access."]}]},{title:"Send a useful diagnostic",items:[{question:"What should a microphone bug report include?",paragraphs:["Use Copy Diagnostic in the Microphone Interrupted or take-processing message. The report contains technical state such as permission, track status, recorder mode and browser identification; it does not include the recorded voice audio.","Add whether you were in Single Player or Multiplayer, host or guest, the Dub Mod name, the exact step that failed, device and operating system, and whether Mic Check could record and play back sound."],links:[{label:"Open the full bug-report checklist",href:"/guide/faq"}]}]}]},{id:"faq",kicker:"FREQUENTLY ASKED QUESTIONS",title:"Voxalike FAQ",body:"Answers to common questions from the Voxalike community about playing, Workshop mods, multiplayer, microphones, exports, Creator Workshop and contributors. Updated from recent Discord support conversations and last reviewed for Voxalike v1.4.6.",faqGroups:[{title:"Playing and browser support",items:[{question:"Can I play Voxalike on a phone or tablet?",paragraphs:["Yes. Voxalike supports landscape layouts for phones, tablets and Chromebook-style devices. Current Chrome or Edge is recommended, and installing Voxalike from the browser can provide a more game-like full-screen experience.","Mobile support is still being improved. If a layout or control does not fit your screen, report it in the Discord mobile feedback section and include a screenshot, device model and browser."]},{question:"Do I need an account to play?",paragraphs:["No. You can play with local mods without an account. An account is needed for Workshop subscriptions, ratings, uploads and account benefits."]},{question:"Which interface languages are available?",paragraphs:["Voxalike supports English, German, Dutch, Russian and Ukrainian. Change the language in Settings. Creator Workshop follows the same global language setting.","Mod captions, clip titles and character names come from the mod itself and are not automatically translated. Use a mod that contains captions in the language you want."]},{question:"Why does Voxalike work in a private window but not my normal browser?",paragraphs:["A browser extension, translator, injected overlay or old cached script may be changing the page. Disable extensions for voxalike.com and avoid using a browser translator on the game page. Voxalike already includes its own supported interface languages."]}]},{title:"Workshop mods and multiplayer",items:[{question:"What is the difference between Subscribe, Install and Import?",bullets:["Subscribe saves a Workshop mod to your account. Open Mods from the PLAY home screen and wait for it to finish installing in this browser. Press Refresh Mods in the upper-left corner to check
153subscriptions and updates immediately.","Unsubscribe removes the Workshop subscription and its installed copy.","Import Mod is only for a ZIP or folder stored on your own device."],paragraphs:["Your installed and subscribed mods are shown together under Mods.","Workshop subscriptions are tied to your account, but playable files are stored in each browser. Clearing site data removes installed files; subscribed Workshop mods can be reinstalled, while local mods must be imported again."],links:[{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Why is Ready Up waiting on Installing room mod?",paragraphs:["The host selected a Workshop mod that your browser does not have yet. Voxalike is downloading and installing the same mod for you. Wait for the progress indicator to finish, then Ready Up.","If installation remains stuck, open Mods, press Refresh Mods, then return to the room and Ready Up again. Sending the room code and mod name helps when reporting the problem."],links:[{label:"How to Play",href:"/guide/how-to-play"},{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Can we use a local mod in multiplayer?",paragraphs:["Yes, but every player must import the same local mod. Voxalike cannot download a private local ZIP from the host's device.","For Workshop mods, guests can automatically install the host's selected mod instead."]},{question:"Why does a Choicer Voicer mod show only pictures, freeze, or fail at the result?",paragraphs:["The pack probably uses OGV video. Voxalike blocks these original packs during import. Convert the video files inside the pack to MP4 first, then import the converted ZIP or folder.","Workshop Dub Mods are checked and converted during upload, so this warning mainly applies to local imports."]},{question:"Workshop shows a D1 overloaded error. What should I do?",paragraphs:["This means a traffic spike temporarily overloaded the Workshop database; it is not a problem with your mod or account. Voxalike v1.4.5 substantially reduced database traffic and serves more public Workshop data from cached snapshots.","Wait briefly and refresh once. If the error continues, report the time and a screenshot instead of repeatedly refreshing the page."]},{question:"I accidentally continued past a line in Co-op. Do we have to restart?",paragraphs:["In Co-op Claim Mode, the host can open Host Tools and use Redo last line. Host Tools also shows recovery actions for a line that is currently stuck or claimed when those actions are applicable."],links:[{label:"How to Play",href:"/guide/how-to-play"}]}]},{title:"Multiplayer and recovery",items:[{question:"What is the difference between Co-op and PVP?",paragraphs:["Co-op creates one shared dub, with different players performing different lines, and supports up to 12 players. PVP compares player performances and supports up to 8 players.","PVP offers Turn Battle, where players record in sequence, and Parallel Race, where everyone completes the clip list independently while competing on accuracy and completion time."],links:[{label:"How to Play",href:"/guide/how-to-play"}]},{question:"Can Multiplayer lines be assigned by character?",paragraphs:["There is no automatic character assignment. In Co-op, use Claim Mode so the group can decide who records each open line. Round Robin instead rotates turns in order."]},{question:"What happens if my connection drops or the game server updates?",paragraphs:["Keep the page open and allow Voxalike to reconnect. A normal network interruption is shown as Connection interrupted. A deployment is shown as Game server updated, and the room is restored automatically when possible. Locally saved takes can be recovered with the room state.","Do not deliberately leave or close the room while recovery is in progress."]},{question:"Where can I find a recent Multiplayer result?",paragraphs:["Open Recent Dubs from the Create or Join Room screen. It can restore up to five completed results in the same browser for up to seven days when their local recording copies finished saving.","Download important results promptly because clearing site data, using another browser profile or automatic storage cleanup removes access to the local copy."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]}]},{title:"Microphone and recording",items:[{question:"Record or Mic Check does nothing. What should I do?",paragraphs:["Open the site-permissions icon beside the address bar and allow Microphone for voxalike.com. Also check the operating system's microphone permission for your browser, select the correct input in Settings, run Mic Check and fully restart the browser after changing permission.","Chrome or Edge is recommended. If Yandex Browser says permission is granted but cannot create a microphone stream, select the real microphone instead of Default in Yandex site settings, or try Chrome or Edge."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]},{question:"Mic Check works, but my microphone stops working during a round. What should I do?",paragraphs:["Tap Try Again when Voxalike reports Microphone Interrupted. Current versions detect disconnected, silent or stale microphone streams instead of silently saving an empty take.","If it repeats, use Copy Diagnostic and include the report with your browser, device, game mode and mod name. Also check whether a VPN, proxy or security filter is interfer
153ing with the secure microphone connection."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]},{question:"Why did Voxalike make a quiet line louder or a scream quieter?",paragraphs:["Use Natural microphone processing for the most voice detail. Clean voice applies stronger noise reduction for noisy rooms, but it may affect very quiet word endings.","For the best result, run Mic Check in a quiet room, stay silent during the noise sample, then speak at your normal volume and apply the result."]}]},{title:"Results, recovery and video export",items:[{question:"Where is my finished dubbed video?",paragraphs:["After the last recording, Voxalike premieres the completed dub, shows the judges, and then opens the result screen. Use My Shorts Video, My Landscape Video or My Audio Track to download your work."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]},{question:"Why are exported videos WebM instead of MP4?",paragraphs:["WebM is more reliable for long browser-side rendering. Converting to MP4 inside the browser would use much more CPU and memory, while server conversion would add significant operating cost. Chrome is recommended for WebM playback; convert the file separately if you need MP4."]},{question:"Why did video export stay on Starting, freeze or fail?",paragraphs:["Long videos and low-memory systems can overload browser rendering, especially if the game is backgrounded or other heavy apps are running. Keep the Voxalike tab open and visible, close unnecessary apps, let the preview load and use Cancel & Retry when Voxalike detects a stalled export stage.","On mobile, the final save must be started by a player tap because browsers block automatic downloads. Very long mods may still exceed a device's available memory.","Local Choicer Voicer packs that still use OGV video are blocked during import. Convert their video files to MP4 before importing them into Voxalike.","If the same point fails every time, report the mod name, browser and device, approximate timestamp, room code when applicable and the affected WebM file if possible."]},{question:"Can I close the tab while a video is downloading?",paragraphs:["No. Video rendering happens locally in your browser. Keep the tab open until the download starts and finishes. Closing the tab cancels work that has not completed."],links:[{label:"Open the Voxalike FAQ",href:"/guide/faq"}]},{question:"I cannot scroll through all Clip Scores. What should I do?",paragraphs:["This was fixed in v1.2.5. Refresh the site so you are running the latest version. If it still happens, include your browser, screen size and a screenshot in the bug report."]}]},{title:"Creator Workshop and upload limits",items:[{question:"How do I make and upload a Dub Mod or Judge Mod?",paragraphs:["Sign in, open your profile menu and choose Creator Workshop. Add a playable ZIP, review its listing information and publish it. Creator Workshop follows the language selected in the main Voxalike Settings.","Read the Voxalike mod guides or use the community-made Voxalike Mod Maker. Use MP4 or WebM video for Dub Mods. Voxalike blocks local OGV packs until their video is converted to MP4."],links:[{label:"Dub Mod Guide",href:"/guide/make-dub-mods"},{label:"Judge Pack Format",href:"/docs/judge-pack-format"},{label:"Voxalike Mod Maker",href:"https://app.voxalike-mod-maker.workers.dev/",note:"Community-made tool"}]},{question:"How large can a Dub Mod be?",paragraphs:["The current Dub Mod ZIP limit is 800 MB, and each individual audio clip must be 60 seconds or shorter. Workshop checks clip durations before uploading and identifies the file if a clip is too long or cannot be read.","Very large mods require more browser memory and take longer to install, so splitting a full movie or very long scene into smaller packs is strongly recommended. Judge Mod ZIP files have a separate 30 MB limit."]},{question:"How many Workshop mods can I publish?",paragraphs:["Accounts less than 7 days old can publish 7 new mods per rolling 24 hours. Accounts at least 7 days old can publish 20, and approved Community Contributors can publish 50.","Replacing an existing mod does not count as a new publication. An unfinished upload can temporarily occupy a slot for up to two hours; cancel it from My Mods or wait for automatic cleanup."]},{question:"How do I know whether an upload failed?",paragraphs:["Voxalike v1.4.5 keeps a clear failure message on screen and does not present a failed upload as published. Check the message, correct the indicated problem and retry.","Failed or abandoned multipart uploads are cleaned automatically, and unfinished uploads can be cancelled from My Mods."]},{question:"Can I edit a mod after uploading it?",paragraphs:["Yes. Open Creator Workshop, then My Mods. Public text fields cannot contain external links."],bullets:["Edit Details changes the title, creator credit, description, language, tags, uploader visibility and thumbnail.","Replace ZIP uploads a corrected package while keeping the existing Workshop page, subscriptions, ratings and download count.","Hide removes the mod from public Workshop and cancels subscriptions. Permanent deleti
153on becomes available 7 days after hiding."]},{question:"Can I choose a custom Workshop thumbnail?",paragraphs:["Yes. Go to My Uploads, then Edit Details. You can keep the current image, upload JPG, PNG or WebP artwork up to 8 MB, or choose a frame from the preview video.","The custom thumbnail is optional. On first upload, Voxalike uses cover information from the pack when available; otherwise it creates a fallback thumbnail."]},{question:"Why does the creator show as Unknown?",paragraphs:["For Dub Mods, creator credit is first read from _author.txt or _pack_info.ini inside the ZIP. If the pack has no author information, Workshop shows Unknown. The uploader can correct the creator credit later from My Uploads, then Edit Details."]},{question:"Can I upload a mod made by someone else?",paragraphs:["Only upload content you have the rights or permission to share. Crediting the original creator does not replace permission. Duplicate, misleading or unauthorized uploads may be removed, and repeated abuse may restrict Workshop upload access."]}]},{title:"Community Contributor",items:[{question:"What does no reuploads mean for Community Contributor applications?",paragraphs:["You cannot repost a mod created by someone else and count it as your contribution. A mod you originally created for Choicer Voicer can qualify after you publish it on Voxalike, provided it meets the quality, originality and content requirements."]},{question:"How does the Community Contributor application score work?",paragraphs:["Applications need at least 5 verified points. Original high-quality Dub Mods give provisional points at 3, 5 and 7 eligible packs. Each automatic candidate needs at least 10 clips and 30 seconds of video, but originality and quality are reviewed manually.","Public Voxalike videos and other substantial contributions can also provide points. Approved Contributors receive Ad-Free use, an in-game badge and the option to claim the official Discord role after connecting Discord."],links:[{label:"Open Community Contributor",href:"/contributor"}]}]},{title:"Safety and bug reports",items:[{question:"What should I include in a bug report?",paragraphs:["For export problems, also include the approximate timestamp where the video fails and the WebM file if you are comfortable sharing it.","For account-specific upload or Contributor problems, send sensitive account details privately to the developer instead of posting an email address publicly."],bullets:["Game mode and whether you were host or guest","Mod name and whether it came from Workshop or a local ZIP","Browser, device and operating system","What you expected and what happened","Screenshot or short recording","A copied Voxalike diagnostic or console error, if available","Room code for an active multiplayer problem"]}]}]}]},jS=new Set(["dub-pack-format","judge-pack-format"]),HE=new Set(["install-workshop-mods","multiplayer","recover-recent-dubs","microphone-troubleshooting"]),JE=qi.sections.filter(e=>!jS.has(e.id)&&!HE.has(e.id)),Mc={title:"Technical Documentation",seoTitle:"Voxalike Technical Documentation",description:"Technical references for Voxalike pack formats.",sections:qi.sections.filter(e=>jS.has(e.id))},xh=[{path:"/guide",label:"Voxalike Guide",sectionId:void 0},{path:"/guide/how-to-play",label:"How to Play Voxalike",sectionId:"how-to-play"},{path:"/guide/make-dub-mods",label:"How to Make a Dub Pack",sectionId:"make-dub-mods"},{path:"/guide/upload-mod-to-workshop",label:"How to Upload a Mod to Workshop",sectionId:"upload-mod-to-workshop"},{path:"/guide/faq",label:"Voxalike FAQ",sectionId:"faq"}],Rh=[{path:"/docs",label:"Technical Documentation",sectionId:void 0},{path:"/docs/dub-pack-format",label:"Dub Pack Format Reference",sectionId:"dub-pack-format"},{path:"/docs/judge-pack-format",label:"Judge Pack Format Reference",sectionId:"judge-pack-format"}],_E={"/guide":qi.description,"/guide/how-to-play":"Learn how to find Dub Packs, play Voxalike solo or with friends, record each line, watch your finished dub, and download the result.","/guide/make-dub-mods":"Learn how to use the Mod Maker to split an MP4 video into clips, assign characters and dialogue, set the mod information and export a Dub Pack.","/guide/upload-mod-to-workshop":"Learn how to upload a mod ZIP, complete its Workshop listing, choose the correct language, publish it and edit it later from My Mods.","/guide/faq":"Find answers about playing Voxalike, Workshop mods, multiplayer rooms, microphones, WebM video exports and uploading community mods."},YE={"/docs":Mc.description,"/docs/dub-pack-format":"Technical reference for developers building Voxalike Dub Pack creation tools, including the ZIP structure, metadata, audio, video and line fields.","/docs/judge-pack-format":"Technical reference for Voxalike Judge Packs, including judge_pack.json, four judge image slots, custom names, feedback lines and reaction sounds."},KE={fr:{"Voxalike Guide":"Guide Voxalike","Learn how to play Voxalike and make mods.":"Apprenez à jouer à Voxalike et à créer des mods.","PLAYER GUIDE":"GUIDE DU JOUEUR",Guide:"Guider","All guides":"Tous les guides","FOR DEVELOPERS":"POUR LES DÃVELOPPEURS","Technical Documentation":"Documentation technique","Dub Pack and Judge Pack format references.":"Références aux formats Dub Pack et Judge Pack.","HOW TO PLAY":"COMMENT JOUER","Choose a Dub Pack and make the scene yours.":"Choisissez un Dub Pack et personnalisez la scène.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Un Dub Pack est une scène divisée en lignes que vous pouvez jouer. Trouvez-en un dans Workshop, choisissez comment vous souhaitez jouer, puis remplacez chaque ligne originale par votre propre voix. La page Mods est simplement l'endroit où vou
153s pouvez voir et gérer les Dub Packs et les Judge Mods enregistrés dans votre navigateur.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"Ouvrez Workshop dans la navigation supérieure, choisissez un Dub Pack et sélectionnez S'abonner. Revenez ensuite à l'écran d'accueil PLAY, ouvrez Mods et attendez que le pack termine l'installation dans votre navigateur. Il n'y a pas de fichier distinct à télécharger sur votre appareil. Vous pouvez également importer un ZIP local depuis Mods.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"Avant votre première partie, ouvrez Paramètres et exécutez Mic Check. Appliquez le résultat pour un meilleur nettoyage de la voix pendant l'enregistrement.","Open PLAY and choose Single Player or Multiplayer.":"Ouvrez PLAY et choisissez Solo ou Multijoueur.","For Single Player, select one of your installed Dub Packs and start the pack.":"Pour Single Player, sélectionnez lâun de vos Dub Packs installés et démarrez le pack.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Pour le multijoueur, créez ou rejoignez une salle avec des amis. L'hôte choisit Co-op ou PVP, sélectionne un Dub Pack et démarre la partie.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"Pour chaque réplique, regardez la scène et écoutez l'original. Lorsque vous êtes prêt, enregistrez votre version avant la fin de la ligne. Vous pouvez rejouer ou réessayer une prise avant de continuer.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"Complétez le Dub Pack pour regarder votre doublage terminé, voir les résultats et télécharger votre vidéo ou audio.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Menu principal Voxalike avec le bouton Mods sous Solo et Multijoueur","Where do I manage my Dub Packs?":"Où puis-je gérer mes Dub Packs ?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"Revenez à lâécran dâaccueil PLAY et sélectionnez Mods. C'est ici que vous pouvez vérifier les installations, gérer les Dub Packs souscrits et importer des packs locaux.","MAKE A DUB MOD":"FAIRE UN MOD DUB","How to Make a Dub Pack":"Comment créer un pack de doublage","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"Tout dâabord, ouvrez Mod Maker et faites-y glisser un fichier MP4, comme indiqué ci-dessous.","This is the Dub Mod Maker Tool â":"Ceci est l'outil Dub Mod Maker â","Created by Brunoxx.":"Créé par Brunoxx.","Technical documentation":"Documentation technique","For the Dub Pack structure and technical details â":"Pour la structure du Dub Pack et les détails techniques â","Mod Maker Media and timeline page showing where to drag an MP4 file":"Mod Maker Media et page de chronologie montrant où faire glisser un fichier MP4","Drag an MP4 file into the Mod Maker.":"Faites glisser un fichier MP4 dans Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"Ensuite, nous devons diviser la vidéo en clips, puis ajouter un personnage et le dialogue correspondant à chaque clip.","How to split the video into clips":"Comment diviser la vidéo en clips","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"Le bouton Démarrer est utilisé pour placer des marqueurs. Le premier marqueur devient le début du premier clip.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Pour terminer le clip en cours, appuyez à nouveau sur Démarrer. Une section pour le clip correspondant apparaîtra alors sous Lignes.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"Le bouton Fin place un marqueur à la fin de la vidéo. Ce marqueur devient la fin du clip final.","How to assign characters to clips":"Comment attribuer des caractères aux clips","Under Characters, enter the names of all characters used in the Dub Pack.":"Sous Personnages, saisissez les noms de tous les personnages utilisés dans le Dub Pack.","Then go to Lines and select the corresponding character for each clip.":"Allez ensuite dans Lignes et sélectionnez le caractère correspondant pour chaque clip.","How to separate the voice and background audio":"Comment séparer la voix et l'audio de fond","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"Cliquez sur Diviser la voix et l'arrière-plan, comme indiqué ci-dessous. L'outil séparera automatiquement la voix et l'audio de fond pour toute la vidéo.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Chronologie de Mod Maker montrant la voix et l'arrière-plan divisés, les boutons Début et Fin","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"Utilisez Diviser la voix et l'arrière-plan pour séparer l'audio. Utilisez Début et Fin pour marquer les clips.","How to set the mod title and author":"Comment définir le titre et l'auteur du mod","Enter the mod title, author and other information under Mod setup.":"Entrez le titre du mod, l'auteur et d'autres informations sous Configuration du module.","The complete process":"Le processus complet","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"Faites glisser un fichier MP4 dans Mod Maker. Cliquez sur Diviser la voix et l'arrière-plan pour séparer la voix et l'audio d'arrière-plan. Utilisez Début pour diviser la vidéo en clips, puis utilisez Fin pour marquer la fin du clip final.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"Sous Lignes, ajoutez le personnage et le dialogue correspondants à chaque clip. Complétez ensuite les informations de base sur le mod sous Configuration du module.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"Enfin, ouvrez Validation et exportation, vérifiez s'il manque des informations et exportez le Dub Pack.","WORKSHOP UPLOAD":"TÃLÃCHARGEMENT DE L'ATELIER","How to Upload a Mod to Workshop":"Comment télécharger un mod sur l'atelier","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Sur la page d'accueil du jeu, cliquez sur votre avatar dans le coin supérieur droit, puis sélectionnez Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"Page de téléchargement du module Creator Workshop avec le mod ZIP, répertoriant les détails et les champs de langue","Upload Mod in Creator Workshop.":"Téléchargez le mod dans Creator Workshop.","Upload the ZIP":"Téléchargez le ZIP","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"Choisissez Dub Mod ou Judge Mod, puis sélectionnez le mod ZIP depuis votre appareil.","Complete the Workshop listing":"Complétez la liste des ateliers","Fill in the title, description and tags, then choose the language spoken in the mod.":"Remplissez le titre, la description et les balises, puis choisissez la langue parlée dans le mod.","Choose the correct language":"Choisissez la bonne langue","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Choisissez la bonne langue pour votre mod. Sinon, les joueurs souhaitant jouer au mod pourraient lâignorer.","If you selected the wrong language, open My Mods and edit the mod information.":"Si vous avez sélectionné la mauvaise langue, ouvrez Mes Mods et modifiez les informations du mod.","Publish the mod":"Publier le mod","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"Confirmez que vous disposez des droits ou de l'autorisation pour partager le contenu, puis sélectionnez Publier le module.","Edit the mod later":"Modifiez le mod plus tard","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"Après la publication, ouvrez Mes Mods si vous devez modifier la liste ou remplacer le ZIP."}
153,it:{"Voxalike Guide":"Voxalike Guida","Learn how to play Voxalike and make mods.":"Scopri come giocare a Voxalike e creare mod.","PLAYER GUIDE":"GUIDA DEL GIOCATORE",Guide:"Guida","All guides":"Tutte le guide","FOR DEVELOPERS":"PER GLI SVILUPPATORI","Technical Documentation":"Documentazione tecnica","Dub Pack and Judge Pack format references.":"Riferimenti ai formati Dub Pack e Judge Pack.","HOW TO PLAY":"COME GIOCARE","Choose a Dub Pack and make the scene yours.":"Scegli un Dub Pack e rendi la scena tua.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Un Dub Pack è una scena divisa in linee da eseguire. Trovane uno in Workshop, scegli come vuoi giocare, quindi sostituisci ogni riga originale con la tua voce. La pagina Mod è semplicemente il luogo in cui puoi vedere e gestire i Dub Pack e i Judge Mod salvati nel tuo browser.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"Apri Workshop dalla navigazione in alto, scegli un Dub Pack e seleziona Iscriviti. Quindi torna alla schermata principale di PLAY, apri Mods e attendi che il pacchetto finisca l'installazione nel tuo browser. Non esiste un file separato da scaricare sul tuo dispositivo. Puoi anche importare un ZIP locale da Mods.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"Prima della prima partita, apri Impostazioni ed esegui Mic Check. Applica il risultato per una migliore pulizia vocale durante la registrazione.","Open PLAY and choose Single Player or Multiplayer.":"Apri GIOCA e scegli Giocatore singolo o Multigiocatore.","For Single Player, select one of your installed Dub Packs and start the pack.":"Per Giocatore singolo, seleziona uno dei Dub Pack installati e avvia il pacchetto.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Per il multiplayer, crea o unisciti a una stanza con gli amici. L'host sceglie Co-op o PVP, seleziona un Dub Pack e avvia il gi
153oco.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"Per ogni riga, guarda la scena e ascolta l'originale. Quando sei pronto, registra la tua versione prima che la riga finisca. Puoi riprodurre o riprovare una ripresa prima di continuare.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"Completa il Dub Pack per guardare il tuo doppiaggio finito, vedere i risultati e scaricare il tuo video o audio.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Menu principale Voxalike con il pulsante Mod sotto Giocatore singolo e Multigiocatore","Where do I manage my Dub Packs?":"Dove gestisco i miei Dub Pack?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"Torna alla schermata iniziale PLAY e seleziona Mod. Qui è dove puoi controllare le installazioni, gestire i Dub Pack abbonati e importare i pacchetti locali.","MAKE A DUB MOD":"REALIZZA UN MOD DUB","How to Make a Dub Pack":"Come creare un pacchetto doppiaggio","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"Innanzitutto, apri Mod Maker e trascina un file MP4 al suo interno, come mostrato di seguito.","This is the Dub Mod Maker Tool â":"Questo è lo strumento Dub Mod Maker â","Created by Brunoxx.":"Creato da Brunoxx.","Technical documentation":"Documentazione tecnica","For the Dub Pack structure and technical details â":"Per la struttura e i dettagli tecnici del Dub Pack â","Mod Maker Media and timeline page showing where to drag an MP4 file":"Mod Maker Pagina multimediale e timeline che mostra dove trascinare un file MP4.","Drag an MP4 file into the Mod Maker.":"Trascina un file MP4 nel Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"Successivamente, dobbiamo dividere il video in clip, quindi aggiungere un personaggio e il dialogo corrispondente a ciascuna clip.","How to split the video into clips":"Come dividere il video in clip","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"Il pulsante Start viene utilizzato per posizionare i marcatori. Il primo marcatore diventa l'inizio della prima clip.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Per terminare la clip corrente, premere nuovamente Avvia. Sotto Linee verrà quindi visualizzata una sezione per la clip corrispondente.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"Il pulsante Fine inserisce un marcatore alla fine del video. Questo marcatore diventa la fine della clip finale.","How to assign characters to clips":"Come assegnare caratteri alle clip","Under Characters, enter the names of all characters used in the Dub Pack.":"In Personaggi, inserisci i nomi di tutti i personaggi utilizzati nel Dub Pack.","Then go to Lines and select the corresponding character for each clip.":"Quindi vai su Linee e seleziona il carattere corrispondente per ogni clip.","How to separate the voice and background audio":"Come separare la voce e l'audio di sottofondo","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"Fai clic su Dividi voce e sfondo, come mostrato di seguito. Lo strumento separerà automaticamente la voce e l'audio di sottofondo per l'intero video.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Timeline di Mod Maker che mostra voce e sfondo divisi, pulsanti Inizio e Fine","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"Utilizza Dividi voce e sfondo per separare l'audio. Utilizza Inizio e Fine per contrassegnare le clip.","How to set the mod title and author":"Come impostare il titolo e l'autore della mod","Enter the mod title, author and other information under Mod setup.":"Inserisci il titolo della mod, l'autore e altre informazioni in Configurazione mod.","The complete process":"Il processo completo","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"Trascina un file MP4 nel Mod Maker. Fare clic su Dividi voce e sottofondo per separare la voce e l'audio di sottofondo. Utilizza Inizio per dividere il video in clip, quindi utilizza Fine per contrassegnare la fine della clip finale.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"In Linee, aggiungi il carattere e il dialogo corrispondenti a ciascuna clip. Quindi completa le informazioni di base sulla mod in Configurazione mod.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"Infine, apri Validazione ed esportazione, controlla se mancano informazioni ed esporta il Dub Pack.","WORKSHOP UPLOAD":"CARICAMENTO DEL WORKSHOP","How to Upload a Mod to Workshop":"Come caricare una mod su Workshop","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Nella home page del gioco, fai clic sul tuo avatar nell'angolo in alto a destra, quindi seleziona Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"Pagina della mod di caricamento del Creator Workshop con la mod ZIP, i dettagli dell'elenco e i campi della lingua","Upload Mod in Creator Workshop.":"Carica la mod nel Creator Workshop.","Upload the ZIP":"Carica il ZIP","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"Scegli Dub Mod o Judge Mod, quindi seleziona la mod ZIP dal tuo dispositivo.","Complete the Workshop listing":"Completa l'elenco dei workshop","Fill in the title, description and tags, then choose the language spoken in the mod.":"C
153ompila il titolo, la descrizione e i tag, quindi scegli la lingua parlata nel mod.","Choose the correct language":"Scegli la lingua corretta","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Scegli la lingua corretta per il tuo mod. Altrimenti, i giocatori che vogliono giocare alla mod potrebbero ignorarla.","If you selected the wrong language, open My Mods and edit the mod information.":"Se hai selezionato la lingua sbagliata, apri Le mie mod e modifica le informazioni sulla mod.","Publish the mod":"Pubblica il mod","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"Conferma di avere i diritti o l'autorizzazione per condividere il contenuto, quindi seleziona Pubblica mod.","Edit the mod later":"Modifica la mod più tardi","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"Dopo la pubblicazione, apri Le mie modifiche se devi modificare l'elenco o sostituire il ZIP."},es:{"Voxalike Guide":"Voxalike GuÃa","Learn how to play Voxalike and make mods.":"Aprende a jugar Voxalike y a hacer modificaciones.","PLAYER GUIDE":"GUÃA DEL JUGADOR",Guide:"GuÃa","All guides":"Todas las guÃas","FOR DEVELOPERS":"PARA DESARROLLADORES","Technical Documentation":"Documentación técnica","Dub Pack and Judge Pack format references.":"Referencias de formato Dub Pack y Judge Pack.","HOW TO PLAY":"CÃMO JUGAR","Choose a Dub Pack and make the scene yours.":"Elige un Dub Pack y haz tuya la escena.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Un Dub Pack es una escena dividida en lÃneas para que la interpretes. Encuentra uno en Workshop, elige cómo quieres jugar y luego reemplaza cada lÃnea original con tu propia voz. La página Mods es simplemente donde puedes ver y administrar los Dub Packs y Judge Mods guardados en tu navegador.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"Abra Workshop desde la navegación superior, elija un Dub Pack y seleccione Suscribirse. Luego regresa a la pantalla de inicio de PLAY, abre Mods y espera a que el paquete termine de instalarse en tu navegador. No hay ningún archivo separado para descargar a su dispositivo. También puedes importar un ZIP local desde Mods.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"Antes de tu primer juego, abre Configuración y ejecuta Mic Check. Aplique el resultado para una mejor limpieza de voz mientras graba.","Open PLAY and choose Single Player or Multiplayer.":"Abre JUGAR y elige Un jugador o Multijugador.","For Single Player, select one of your installed Dub Packs and start the pack.":"Para un jugador, seleccione uno de sus Dub Packs instalados e inicie el paquete.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Para multijugador, crea o únete a una sala con amigos. El anfitrión elige Cooperativo o PvP, selecciona un Dub Pack y comienza el juego.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"Para cada lÃnea, mira la escena y escucha el original. Cuando esté listo, grabe su versión antes de que termine la lÃnea. Puedes reproducir o reintentar una toma antes de continuar.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"Complete el Dub Pack para ver su doblaje terminado, ver los resultados y descargar su video o audio.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Vox
153alike menú principal con el botón Mods debajo de Un jugador y Multijugador","Where do I manage my Dub Packs?":"¿Dónde administro mis Dub Packs?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"Regrese a la pantalla de inicio de PLAY y seleccione Mods. Aquà es donde puedes comprobar las instalaciones, gestionar los Dub Packs suscritos e importar paquetes locales.","MAKE A DUB MOD":"HACER UN MODO DE DOBLAR","How to Make a Dub Pack":"Cómo hacer un paquete de doblaje","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"Primero, abre Mod Maker y arrastra un archivo MP4 hacia él, como se muestra a continuación.","This is the Dub Mod Maker Tool â":"Esta es la herramienta Dub Mod Maker â","Created by Brunoxx.":"Creado por Brunoxx.","Technical documentation":"Documentación técnica","For the Dub Pack structure and technical details â":"Para la estructura del Dub Pack y detalles técnicos â","Mod Maker Media and timeline page showing where to drag an MP4 file":"Mod Maker Media y página de lÃnea de tiempo que muestra dónde arrastrar un archivo MP4","Drag an MP4 file into the Mod Maker.":"Arrastra un archivo MP4 al Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"A continuación, debemos dividir el vÃdeo en clips y luego agregar un personaje y el diálogo correspondiente a cada clip.","How to split the video into clips":"Cómo dividir el vÃdeo en clips","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"El botón Inicio se utiliza para colocar marcadores. El primer marcador se convierte en el comienzo del primer clip.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Para finalizar el clip actual, presione Iniciar nuevamente. Luego aparecerá una sección para el clip correspondiente en LÃneas.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"El botón Finalizar coloca un marcador al final del vÃdeo. Este marcador se convierte en el final del clip final.","How to assign characters to clips":"Cómo asignar caracteres a clips","Under Characters, enter the names of all characters used in the Dub Pack.":"En Personajes, ingresa los nombres de todos los personajes utilizados en el Dub Pack.","Then go to Lines and select the corresponding character for each clip.":"Luego vaya a LÃneas y seleccione el carácter correspondiente para cada clip.","How to separate the voice and background audio":"Cómo separar la voz y el audio de fondo","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"Haga clic en Dividir voz y fondo, como se muestra a continuación. La herramienta separará automáticamente la voz y el audio de fondo de todo el vÃdeo.","Mod Maker timeline showing Split voice and background, Start and End buttons":"LÃnea de tiempo de Mod Maker que muestra voz y fondo divididos, botones de inicio y fin","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"Utilice Dividir voz y fondo para separar el audio. Utilice Inicio y Fin para marcar los clips.","How to set the mod title and author":"Cómo configurar el tÃtulo y el autor del mod","Enter the mod title, author and other information under Mod setup.":"Ingrese el tÃtulo del mod, el autor y otra información en Configuración del mod.","The complete process":"El proceso completo","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"Arrastra un archivo MP4 al Mod Maker. Haga clic en Dividir voz y fondo para separar la voz y el audio de fondo. Use Inicio para dividir el video en clips, luego use Fin para marcar el final del clip final.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"En LÃneas, agregue el car
153ácter y el diálogo correspondientes a cada clip. Luego complete la información básica del mod en Configuración del mod.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"Finalmente, abra Validación y exportación, verifique si falta información y exporte el Dub Pack.","WORKSHOP UPLOAD":"SUBIR TALLER","How to Upload a Mod to Workshop":"Cómo cargar un mod al taller","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"En la página de inicio del juego, haz clic en tu avatar en la esquina superior derecha y luego selecciona Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"Página de carga de Creator Workshop con el mod ZIP, detalles de listado y campos de idioma","Upload Mod in Creator Workshop.":"Sube Mod en Creator Workshop.","Upload the ZIP":"Sube el ZIP","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"Elija Dub Mod o Judge Mod, luego seleccione el mod ZIP de su dispositivo.","Complete the Workshop listing":"Completa el listado del Taller","Fill in the title, description and tags, then choose the language spoken in the mod.":"Complete el tÃtulo, la descripción y las etiquetas, luego elija el idioma que se habla en el mod.","Choose the correct language":"Elige el idioma correcto","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Elija el idioma correcto para su mod. De lo contrario, los jugadores que quieran jugar el mod pueden ignorarlo.","If you selected the wrong language, open My Mods and edit the mod information.":"Si seleccionó el idioma incorrecto, abra Mis Mods y edite la información del mod.","Publish the mod":"publicar el mod","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"Confirme que tiene los derechos o permiso para compartir el contenido, luego seleccione Publicar Mod.","Edit the mod later":"Edita el mod más tarde","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"Después de la publicación, abre Mis Mods si necesitas editar el listado o reemplazar el ZIP."},pt:{"Voxalike Guide":"Voxalike Guia","Learn how to play Voxalike and make mods.":"Aprenda como jogar Voxalike e fazer mods.","PLAYER GUIDE":"GUIA DO JOGADOR",Guide:"Guia","All guides":"Todos os guias","FOR DEVELOPERS":"PARA DESENVOLVEDORES","Technical Documentation":"Documentação Técnica","Dub Pack and Judge Pack format references.":"Referências de formato Dub Pack e Judge Pack.","HOW TO PLAY":"COMO JOGAR","Choose a Dub Pack and make the scene yours.":"Escolha um Dub Pack e personalize a cena.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Um Dub Pack é uma cena dividida em falas para você interpretar. Encontre uma no Workshop, escolha como deseja tocar e substitua cada linha original pela sua própria voz. A página Mods é simplesmente onde você pode ver e gerenciar os Dub Packs e Judge Mods salvos em seu navegador.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"Abra o Workshop na navegação superior, escolha um Dub Pack e selecione Inscrever-se. Em seguida, retorne à tela inicial do PLAY, abra Mods e aguarde a conclusão da instalação do pacote em seu navegador. Não há arquivo separado para baixar no seu dispositivo. Você também pode importar um ZIP local dos Mods.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"Antes do primeiro jogo, abra Configurações e execute o Mic Check. Aplique o resultado para uma melhor limpeza de voz durante a gravação.","Open PLAY and choose Single Player or Multiplayer.":"Abra o PLAY e escolha Single Player ou Multiplayer.","For Single Player, select one of your installed Dub Packs and start the pack.":"Para Single Player, selecione um dos Dub Packs instalados e inicie o pacote.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Para Multiplayer, crie ou entre em uma sala com amigos. O anfitrião escolhe Co-op ou PVP, seleciona um Dub Pack e inicia o jogo.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"Para cada linha, assista à cena e ouça o original. Quando estiver pronto, grave sua versão antes que a linha termine. Você pode reproduzir ou tentar novamente uma tomada antes de continuar.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"Complete o Dub Pack para assistir a dublagem finalizada, ver os resultados e baixar seu vÃdeo ou áudio.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Vox
153alike menu principal com o botão Mods abaixo de Single Player e Multiplayer","Where do I manage my Dub Packs?":"Onde posso gerenciar meus Dub Packs?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"Volte para a tela inicial do PLAY e selecione Mods. à aqui que você pode verificar instalações, gerenciar Dub Packs assinados e importar pacotes locais.","MAKE A DUB MOD":"FAÃA UM MOD DUB","How to Make a Dub Pack":"Como fazer um pacote de dublagem","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"Primeiro, abra o Mod Maker e arraste um arquivo MP4 para ele, conforme mostrado abaixo.","This is the Dub Mod Maker Tool â":"Esta é a ferramenta Dub Mod Maker â","Created by Brunoxx.":"Criado por Brunoxx.","Technical documentation":"Documentação técnica","For the Dub Pack structure and technical details â":"Para a estrutura do Dub Pack e detalhes técnicos â","Mod Maker Media and timeline page showing where to drag an MP4 file":"Página de mÃdia e linha do tempo do Mod Maker mostrando para onde arrastar um arquivo MP4","Drag an MP4 file into the Mod Maker.":"Arraste um arquivo MP4 para o Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"A seguir, precisamos dividir o vÃdeo em clipes e, em seguida, adicionar um personagem e o diálogo correspondente a cada clipe.","How to split the video into clips":"Como dividir o vÃdeo em clipes","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"O botão Iniciar é usado para colocar marcadores. O primeiro marcador se torna o inÃcio do primeiro clipe.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Para encerrar o clipe atual, pressione Iniciar novamente. Uma seção para o clipe correspondente aparecerá em Linhas.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"O botão Finalizar coloca um marcador no final do vÃdeo. Este marcador se torna o final do clipe final.","How to assign characters to clips":"Como atribuir caracteres aos clipes","Under Characters, enter the names of all characters used in the Dub Pack.":"Em Personagens, insira os nomes de todos os personagens usados no Dub Pack.","Then go to Lines and select the corresponding character for each clip.":"Em seguida, vá em Linhas e selecione o caractere correspondente para cada clipe.","How to separate the voice and background audio":"Como separar a voz e o áudio de fundo","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"Clique em Dividir voz e plano de fundo, conforme mostrado abaixo. A ferramenta separará automaticamente a voz e o áudio de fundo de todo o vÃdeo.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Linha do tempo do Mod Maker mostrando voz e plano de fundo divididos, botões Iniciar e Finalizar","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"Use Dividir voz e fundo para separar o áudio. Use InÃcio e Fim para marcar os clipes.","How to set the mod title and author":"Como definir o tÃtulo e o autor do mod","Enter the mod title, author and other information under Mod setup.":"Insira o tÃtulo do mod, autor e outras informações em Configuração do Mod.","The complete process":"O processo completo","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"Arraste um arquivo MP4 para o Mod Maker. Clique em Dividir voz e fundo para separar a voz e o áudio de fundo. Use Iniciar para dividir o vÃdeo em clipes e, em seguida, use Fim para marcar o final do clipe final.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"Em Linhas, adicione o personagem e o diálogo correspondentes a cada clipe. Em seguida, preencha as informações básicas do mod em Configuração do Mod.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"Por fim, abra Validação e exportação, verifique se falta alguma
153informação e exporte o Dub Pack.","WORKSHOP UPLOAD":"CARREGAR OFICINA","How to Upload a Mod to Workshop":"Como fazer upload de um mod para o Workshop","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Na página inicial do jogo, clique no seu avatar no canto superior direito e selecione Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"Página de upload do mod do Creator Workshop com o mod ZIP, listando detalhes e campos de idioma","Upload Mod in Creator Workshop.":"Carregue o Mod no Creator Workshop.","Upload the ZIP":"Carregue o ZIP","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"Escolha Dub Mod ou Judge Mod e selecione o mod ZIP do seu dispositivo.","Complete the Workshop listing":"Preencha a listagem do Workshop","Fill in the title, description and tags, then choose the language spoken in the mod.":"Preencha o tÃtulo, a descrição e as tags, depois escolha o idioma falado no mod.","Choose the correct language":"Escolha o idioma correto","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Escolha o idioma correto para o seu mod. Caso contrário, os jogadores que quiserem jogar o mod podem ignorá-lo.","If you selected the wrong language, open My Mods and edit the mod information.":"Se você selecionou o idioma errado, abra Meus Mods e edite as informações do mod.","Publish the mod":"Publique o mod","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"Confirme se você tem direitos ou permissão para compartilhar o conteúdo e selecione Publicar Mod.","Edit the mod later":"Edite o mod mais tarde","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"Após a publicação, abra Meus Mods se precisar editar a listagem ou substituir o ZIP."}},ZE={...KE,de:{"Voxalike Guide":"Voxalike-Anleitung","Learn how to play Voxalike and make mods.":"Erfahre, wie du Voxalike spielst und Mods erstellst.","PLAYER GUIDE":"SPIELERANLEITUNG",Guide:"Anleitung","All guides":"Alle Anleitungen","FOR DEVELOPERS":"FÃR ENTWICKLER","Technical Documentation":"Technische Dokumentation","Dub Pack and Judge Pack format references.":"Formatreferenzen für Dub Packs und Judge Packs.","HOW TO PLAY":"SPIELANLEITUNG","Choose a Dub Pack and make the scene yours.":"Wähle ein Dub Pack und mach die Szene zu deiner eigenen.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Ein Dub Pack ist eine Szene, die in einzelne Zeilen für deine Aufnahme aufgeteilt ist. Suche eines im Workshop, wähle deine Spielweise und ersetze dann jede Originalzeile durch deine eigene Stimme. Auf der Mods-Seite kannst du die in deinem Browser gespeicherten Dub Packs und Judge Mods ansehen und verwalten.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"Ãffne den Workshop über die obere Navigation, wähle ein Dub Pack und klicke auf Subscribe. Kehre dann zum PLAY-Startbildschirm zurück, öffne Mods und warte, bis das Pack in deinem Browser installiert ist. Du musst keine separate Datei auf dein Gerät herunterladen. Unter Mods kannst du auch eine lokale ZIP-Datei importieren.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"Ãffne vor deinem ersten Spiel die Einstellungen und führe den Mic Check aus. Ãbernimm das Ergebnis, um deine Stimme bei der Aufnahme besser zu bereinigen.","Open PLAY and choose Single Player or Multiplayer.":"Ãffne PLAY und wähle Einzelspieler oder Mehrspieler.","For Single Player, select one of your installed Dub Packs and start the pack.":"Wähle im Einzelspieler eines deiner installierten Dub Packs aus und starte es.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Erstelle im Mehrspieler einen Raum mit Freunden oder tritt einem bei. Der Host wählt Co-op oder PVP, wählt ein Dub Pack aus und startet das Spiel.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"Sieh dir bei jeder Zeile die Szene an und höre das Original. Wenn du bereit bist, nimm deine Version auf, bevor die Zeile endet. Vor dem Fortfahren kannst du die Aufnahme erneut abspielen oder wiederholen.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"SchlieÃe das Dub Pack ab, um deine fertige Synchronisation anzusehen, die Ergebnisse zu sehen und dein Video oder Audio herunterzuladen.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Vox
153alike-Hauptmenü mit der Mods-Schaltfläche unter Einzelspieler und Mehrspieler","Where do I manage my Dub Packs?":"Wo verwalte ich meine Dub Packs?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"Kehre zum PLAY-Startbildschirm zurück und wähle Mods. Dort kannst du Installationen prüfen, abonnierte Dub Packs verwalten und lokale Packs importieren.","MAKE A DUB MOD":"DUB-MOD ERSTELLEN","How to Make a Dub Pack":"So erstellst du ein Dub Pack","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"Ãffne zuerst den Mod Maker und ziehe wie unten gezeigt eine MP4-Datei hinein.","This is the Dub Mod Maker Tool â":"Hier ist das Dub Mod Maker Tool â","Created by Brunoxx.":"Erstellt von Brunoxx.","Technical documentation":"Technische Dokumentation","For the Dub Pack structure and technical details â":"Struktur und technische Details des Dub Packs â","Mod Maker Media and timeline page showing where to drag an MP4 file":"Medien- und Zeitleistenseite des Mod Makers mit dem Bereich für eine MP4-Datei","Drag an MP4 file into the Mod Maker.":"Ziehe eine MP4-Datei in den Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"Als Nächstes teilen wir das Video in Clips und weisen jedem Clip eine Figur und den passenden Dialog zu.","How to split the video into clips":"So teilst du das Video in Clips","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"Mit der Schaltfläche Start setzt du Markierungen. Die erste Markierung wird zum Anfang des ersten Clips.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Drücke erneut Start, um den aktuellen Clip zu beenden. Unter Lines erscheint dann ein Bereich für den entsprechenden Clip.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"Die Schaltfläche End setzt eine Markierung am Ende des Videos. Diese Markierung wird zum Ende des letzten Clips.","How to assign characters to clips":"So weist du Clips Figuren zu","Under Characters, enter the names of all characters used in the Dub Pack.":"Trage unter Characters die Namen aller Figuren ein, die im Dub Pack verwendet werden.","Then go to Lines and select the corresponding character for each clip.":"Gehe dann zu Lines und wähle für jeden Clip die passende Figur aus.","How to separate the voice and background audio":"So trennst du Stimme und Hintergrundton","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"Klicke wie unten gezeigt auf Split voice & background. Das Tool trennt automatisch die Stimme und den Hintergrundton des gesamten Videos.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Zeitleiste des Mod Makers mit den Schaltflächen Split voice & background, Start und End","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"Trenne den Ton mit Split voice & background. Markiere die Clips mit Start und End.","How to set the mod title and author":"So legst du Mod-Titel und Autor fest","Enter the mod title, author and other information under Mod setup.":"Trage unter Mod setup den Mod-Titel, den Autor und weitere Informationen ein.","The complete process":"Der vollständige Ablauf","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"Ziehe eine MP4-Datei in den Mod Maker. Klicke auf Split voice & background, um Stimme und Hintergrundton zu trennen. Teile das Video mit Start in Clips und markiere dann mit End das Ende des letzten Clips.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"Füge unter Lines jedem Clip die passende Figur und den Dialog hinzu. Fülle danach unter Mod setup die grundlegenden Mod-Informationen aus.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"Ãffne zum Schluss Validation & export, prüfe, ob Informationen fehlen, und exp
153ortiere das Dub Pack.","WORKSHOP UPLOAD":"WORKSHOP-UPLOAD","How to Upload a Mod to Workshop":"So lädst du einen Mod in den Workshop hoch","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Klicke auf der Startseite des Spiels oben rechts auf deinen Avatar und wähle dann Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"Upload-Mod-Seite des Creator Workshops mit Mod-ZIP, Eintragsdetails und Sprachfeldern","Upload Mod in Creator Workshop.":"Upload Mod im Creator Workshop.","Upload the ZIP":"ZIP-Datei hochladen","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"Wähle Dub Mod oder Judge Mod und dann die Mod-ZIP-Datei auf deinem Gerät aus.","Complete the Workshop listing":"Workshop-Eintrag ausfüllen","Fill in the title, description and tags, then choose the language spoken in the mod.":"Fülle Titel, Beschreibung und Tags aus und wähle dann die Sprache, die im Mod gesprochen wird.","Choose the correct language":"Wähle die richtige Sprache","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Wähle die richtige Sprache für deinen Mod. Andernfalls könnten Spieler, die den Mod spielen möchten, ihn übersehen.","If you selected the wrong language, open My Mods and edit the mod information.":"Wenn du die falsche Sprache ausgewählt hast, öffne My Mods und bearbeite die Mod-Informationen.","Publish the mod":"Mod veröffentlichen","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"Bestätige, dass du die Rechte oder Erlaubnis hast, den Inhalt zu teilen, und klicke dann auf Publish Mod.","Edit the mod later":"Mod später bearbeiten","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"Ãffne nach der Veröffentlichung My Mods, wenn du den Eintrag bearbeiten oder die ZIP-Datei ersetzen möchtest."},nl:{"Voxalike Guide":"Voxalike-handleiding","Learn how to play Voxalike and make mods.":"Leer hoe je Voxalike speelt en mods maakt.","PLAYER GUIDE":"SPELERSHANDLEIDING",Guide:"Handleiding","All guides":"Alle handleidingen","FOR DEVELOPERS":"VOOR ONTWIKKELAARS","Technical Documentation":"Technische documentatie","Dub Pack and Judge Pack format references.":"Formaatreferenties voor Dub Packs en Judge Packs.","HOW TO PLAY":"HOE JE SPEELT","Choose a Dub Pack and make the scene yours.":"Kies een Dub Pack en maak de scène van jou.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Een Dub Pack is een scène die is opgesplitst in zinnen die jij inspreekt. Zoek er een in Workshop, kies hoe je wilt spelen en vervang daarna elke originele zin door je eigen stem. Op de pagina Mods kun je de Dub Packs en Judge Mods bekijken en beheren die in je browser zijn opgeslagen.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"Open Workshop via de navigatie bovenaan, kies een Dub Pack en selecteer Subscribe. Ga daarna terug naar het PLAY-startscherm, open Mods en wacht tot het pack in je browser is geïnstalleerd. Je hoeft geen afzonderlijk bestand naar je apparaat te downloaden. Vanuit Mods kun je ook een lokaal ZIP-bestand importeren.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"Open vóór je eerste spel Settings en voer Mic Check uit. Pas het resultaat toe om je stem tijdens het opnemen beter op te schonen.","Open PLAY and choose Single Player or Multiplayer.":"Open PLAY en kies Single Player of Multiplayer.","For Single Player, select one of your installed Dub Packs and start the pack.":"Selecteer voor Single Player een van je geïnstalleerde Dub Packs en start het pack.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Maak voor Multiplayer een kamer met vrienden of neem eraan deel. De host kiest Co-op of PVP, selecteert een Dub Pack en start het spel.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"Bekijk bij elke zin de scène en luister naar het origineel. Neem je versie op voordat de zin eindigt wanneer je klaar bent. Je kunt een opname opnieuw afspelen of overdoen voordat je verdergaat.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"Voltooi het Dub Pack om je afgewerkte dub te bekijken, de resultaten te zien en je video of audio te downloaden.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Vox
153alike-hoofdmenu met de knop Mods onder Single Player en Multiplayer","Where do I manage my Dub Packs?":"Waar beheer ik mijn Dub Packs?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"Ga terug naar het PLAY-startscherm en selecteer Mods. Hier kun je installaties controleren, geabonneerde Dub Packs beheren en lokale packs importeren.","MAKE A DUB MOD":"EEN DUB-MOD MAKEN","How to Make a Dub Pack":"Een Dub Pack maken","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"Open eerst de Mod Maker en sleep er een MP4-bestand naartoe, zoals hieronder weergegeven.","This is the Dub Mod Maker Tool â":"Hier is de Dub Mod Maker Tool â","Created by Brunoxx.":"Gemaakt door Brunoxx.","Technical documentation":"Technische documentatie","For the Dub Pack structure and technical details â":"Voor de structuur en technische details van een Dub Pack â","Mod Maker Media and timeline page showing where to drag an MP4 file":"De pagina Media en tijdlijn van Mod Maker met de plaats voor een MP4-bestand","Drag an MP4 file into the Mod Maker.":"Sleep een MP4-bestand naar de Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"Vervolgens splitsen we de video in clips en voegen we aan elke clip een personage en de bijbehorende dialoog toe.","How to split the video into clips":"De video in clips splitsen","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"Met de knop Start plaats je markeringen. De eerste markering wordt het begin van de eerste clip.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Druk opnieuw op Start om de huidige clip te beëindigen. Onder Lines verschijnt daarna een gedeelte voor de bijbehorende clip.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"De knop End plaatst een markering aan het einde van de video. Deze markering wordt het einde van de laatste clip.","How to assign characters to clips":"Personages aan clips toewijzen","Under Characters, enter the names of all characters used in the Dub Pack.":"Voer onder Characters de namen in van alle personages die in het Dub Pack worden gebruikt.","Then go to Lines and select the corresponding character for each clip.":"Ga daarna naar Lines en selecteer voor elke clip het bijbehorende personage.","How to separate the voice and background audio":"Stem en achtergrondgeluid scheiden","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"Klik op Split voice & background, zoals hieronder weergegeven. De tool scheidt automatisch de stem en het achtergrondgeluid van de hele video.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Mod Maker-tijdlijn met de knoppen Split voice & background, Start en End","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"Gebruik Split voice & background om het geluid te scheiden. Gebruik Start en End om de clips te markeren.","How to set the mod title and author":"De titel en auteur van de mod instellen","Enter the mod title, author and other information under Mod setup.":"Voer onder Mod setup de titel, auteur en andere informatie van de mod in.","The complete process":"Het volledige proces","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"Sleep een MP4-bestand naar de Mod Maker. Klik op Split voice & background om de stem en het achtergrondgeluid te scheiden. Gebruik Start om de video in clips te splitsen en End om het einde van de laatste clip te markeren.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"Voeg onder Lines aan elke clip het bijbehorende personage en de dialoog toe. Vul daarna onder Mod setup de basisinformatie van de mod in.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"Open ten slotte Validation & export, controleer of er informatie ontbreekt en exporteer het Dub Pack.","WORKSHOP UPLOAD":"WORKSHOP-UPLOAD","How to Upload a Mod to Workshop":"Een mod naar Workshop uploaden","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Klik op de startpagina van het spel rechtsboven op je avatar en selecteer daarna Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"De pagina Upload Mod van Creator Workshop met het mod-ZIP-bestand, vermeldingsgegevens en taalvelden","Upload Mod in Creator Workshop.":"Mod uploaden in Creator Workshop.","Upload the ZIP":"Upload het ZIP-bestand","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"Kies Dub Mod of Judge Mod en selecteer daarna het ZIP-bestand van de mod op je apparaat.","Complete the Workshop listing":"Vul de Workshop-vermelding in","Fill in the title, description and tags, then choose the language spoken in the mod.":"Vul de titel, beschrijving en tags in en kies daarna de taal die in de mod wordt gesproken.","Choose the correct language":"Kies de juiste taal","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Kies de juiste taal voor je mod. Anders kunnen spelers die de mod willen spelen hem overslaan.","If you selected the wrong language, open My Mods and edit the mod information.":"Als je de verkeerde taal hebt geselecteerd, open je My Mods en bewerk je de informatie van de mod.","Publish the mod":"Publiceer de mod","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"Bevestig dat je de rechten of toestemming hebt om de inhoud te delen en selecteer daarna Publish Mod.","Edit the mod later":"Pas de mod later aan","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"Open na publicatie My Mods als je de vermelding wilt aanpassen of het ZIP-bestand wilt vervangen."},ru:{"Voxalike Guide":"Ð ÑководÑÑво Voxalike","Learn how to play Voxalike and make mods.":"УзнайÑе, как игÑаÑÑ Ð² Voxalike и ÑоздаваÑÑ Ð¼Ð¾Ð´Ñ.","PLAYER GUIDE":"РУÐÐÐÐÐСТÐÐ ÐÐЯ ÐÐÐ ÐÐÐÐ",Guide:"Ð ÑководÑÑво","All guides":"ÐÑе ÑÑководÑÑва","FOR DEVELOPERS":"ÐÐЯ Ð ÐÐÐ ÐÐÐТЧÐÐÐÐ","Technical Documentation":"ТеÑ
ниÑеÑÐºÐ°Ñ Ð´Ð¾ÐºÑменÑаÑиÑ","Dub Pack and Judge Pack format references.":"СпÑавоÑнÑе маÑеÑÐ¸Ð°Ð»Ñ Ð¿Ð¾ ÑоÑмаÑам Dub Pack и Judge Pack.","HOW TO PLAY":"ÐÐÐ ÐÐÐ ÐТЬ","Choose a Dub Pack and make the scene yours.":"ÐÑбеÑиÑе Dub Pack и озвÑÑÑÑе ÑÑÐµÐ½Ñ Ð¿Ð¾-ÑвоемÑ.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Dub Pack â ÑÑо ÑÑена, ÑазделÑÐ½Ð½Ð°Ñ Ð½Ð° Ñеплики Ð´Ð»Ñ Ð²Ð°Ñей озвÑÑки. Ð
153айдиÑе Ð¿Ð°ÐºÐµÑ Ð² ÐаÑÑеÑÑкой, вÑбеÑиÑе Ñежим игÑÑ Ð¸ замениÑе каждÑÑ Ð¾ÑигиналÑнÑÑ ÑÐµÐ¿Ð»Ð¸ÐºÑ Ñвоим голоÑом. Ðа ÑÑÑаниÑе «ÐодÑ» можно пÑоÑмаÑÑиваÑÑ Ð¸ ÑпÑавлÑÑÑ Ð¿Ð°ÐºÐµÑами Dub Pack и модами Judge Mod, ÑоÑ
ÑанÑннÑми в бÑаÑзеÑе.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"ÐÑкÑойÑе ÐаÑÑеÑÑкÑÑ Ð² веÑÑ
ней панели, вÑбеÑиÑе Dub Pack и нажмиÑе Subscribe. ÐаÑем веÑниÑеÑÑ Ð½Ð° главнÑй ÑкÑан PLAY, оÑкÑойÑе «ÐодÑ» и дождиÑеÑÑ ÑÑÑановки пакеÑа в бÑаÑзеÑе. ÐÑделÑнÑй Ñайл на ÑÑÑÑойÑÑво ÑкаÑиваÑÑ Ð½Ðµ нÑжно. Ð Ñазделе «ÐодÑ» Ñакже можно импоÑÑиÑоваÑÑ Ð»Ð¾ÐºÐ°Ð»ÑнÑй ZIP-Ñайл.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"ÐеÑед пеÑвой игÑой оÑкÑойÑе наÑÑÑойки и запÑÑÑиÑе Mic Check. ÐÑимениÑе ÑезÑлÑÑаÑ, ÑÑÐ¾Ð±Ñ ÑлÑÑÑиÑÑ Ð¾ÑиÑÑÐºÑ Ð³Ð¾Ð»Ð¾Ñа во вÑÐµÐ¼Ñ Ð·Ð°Ð¿Ð¸Ñи.","Open PLAY and choose Single Player or Multiplayer.":"ÐÑкÑойÑе PLAY и вÑбеÑиÑе одиноÑнÑÑ Ð¸Ð»Ð¸ ÑеÑевÑÑ Ð¸Ð³ÑÑ.","For Single Player, select one of your installed Dub Packs and start the pack.":"РодиноÑной игÑе вÑбеÑиÑе один из ÑÑÑановленнÑÑ
Dub Pack и запÑÑÑиÑе его.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"Ð ÑеÑевой игÑе ÑоздайÑе комнаÑÑ Ñ Ð´ÑÑзÑÑми или пÑиÑоединиÑеÑÑ Ðº ней. ÐедÑÑий вÑбиÑÐ°ÐµÑ Co-op или PVP, вÑбиÑÐ°ÐµÑ Dub Pack и запÑÑÐºÐ°ÐµÑ Ð¸Ð³ÑÑ.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"ÐÐ»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¹ Ñеплики поÑмоÑÑиÑе ÑÑÐµÐ½Ñ Ð¸ пÑоÑлÑÑайÑе оÑигинал. Ðогда бÑдеÑе гоÑовÑ, запиÑиÑе ÑÐ²Ð¾Ñ Ð²ÐµÑÑÐ¸Ñ Ð´Ð¾ оконÑÐ°Ð½Ð¸Ñ Ñеплики. ÐеÑед пÑодолжением запиÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ пÑоÑлÑÑаÑÑ Ð¸Ð»Ð¸ ÑделаÑÑ Ð·Ð°Ð½Ð¾Ð²Ð¾.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"ÐавеÑÑиÑе Dub Pack, ÑÑÐ¾Ð±Ñ Ð¿Ð¾ÑмоÑÑеÑÑ Ð³Ð¾ÑовÑÑ Ð¾Ð·Ð²ÑÑкÑ, ÑвидеÑÑ ÑезÑлÑÑаÑÑ Ð¸ ÑкаÑаÑÑ Ð²Ð¸Ð´ÐµÐ¾ или аÑдио.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Ðлавное Ð¼ÐµÐ½Ñ Voxalike Ñ ÐºÐ½Ð¾Ð¿ÐºÐ¾Ð¹ «ÐодÑ» под одиноÑной и ÑеÑевой игÑой","Where do I manage my Dub Packs?":"Ðде ÑпÑавлÑÑÑ Ð¼Ð¾Ð¸Ð¼Ð¸ Dub Pack?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"ÐеÑниÑеÑÑ Ð½Ð° главнÑй ÑкÑан PLAY и вÑбеÑиÑе «ÐодÑ». ÐдеÑÑ Ð¼Ð¾Ð¶Ð½Ð¾ пÑовеÑиÑÑ ÑÑÑановкÑ, ÑпÑавлÑÑÑ Ð¿Ð¾Ð´Ð¿Ð¸Ñками на Dub Pack и импоÑÑиÑоваÑÑ Ð»Ð¾ÐºÐ°Ð»ÑнÑе пакеÑÑ.","MAKE A DUB MOD":"СÐÐÐÐÐÐÐ DUB-ÐÐÐÐ","How to Make a Dub Pack":"Ðак ÑоздаÑÑ Dub Pack","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"СнаÑала оÑкÑойÑе Mod Maker и пеÑеÑаÑиÑе в него MP4-Ñайл, как показано ниже.","This is the Dub Mod Maker Tool â":"ÐнÑÑÑÑÐ¼ÐµÐ½Ñ Dub Mod Maker наÑ
одиÑÑÑ Ð·Ð´ÐµÑÑ â","Created by Brunoxx.":"Создано Brunoxx.","Technical documentation":"ТеÑ
ниÑеÑÐºÐ°Ñ Ð´Ð¾ÐºÑменÑаÑиÑ","For the Dub Pack structure and technical details â":"СÑÑÑкÑÑÑа Dub Pack и ÑеÑ
ниÑеÑкие подÑобноÑÑи â","Mod Maker Media and timeline page showing where to drag an MP4 file":"СÑÑаниÑа Media and timeline в Mod Maker Ñ Ð¾Ð±Ð»Ð°ÑÑÑÑ Ð´Ð»Ñ Ð¿ÐµÑеÑаÑÐºÐ¸Ð²Ð°Ð½Ð¸Ñ MP4-Ñайла","Drag an MP4 file into the Mod Maker.":"ÐеÑеÑаÑиÑе MP4-Ñайл в Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"ÐаÑем нÑжно ÑазделиÑÑ Ð²Ð¸Ð´ÐµÐ¾ на ÐºÐ»Ð¸Ð¿Ñ Ð¸ добавиÑÑ Ðº ÐºÐ°Ð¶Ð´Ð¾Ð¼Ñ ÐºÐ»Ð¸Ð¿Ñ Ð¿ÐµÑÑонажа и ÑооÑвеÑÑÑвÑÑÑÑÑ ÑепликÑ.","How to split the video into clips":"Ðак ÑазделиÑÑ Ð²Ð¸Ð´ÐµÐ¾ на клипÑ","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"Ðнопка Start ÑÑÐ°Ð²Ð¸Ñ Ð¼ÐµÑки. ÐеÑÐ²Ð°Ñ Ð¼ÐµÑка ÑÑÐ°Ð½ÐµÑ Ð½Ð°Ñалом пеÑвого клипа.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"ЧÑÐ¾Ð±Ñ Ð·Ð°Ð²ÐµÑÑиÑÑ ÑекÑÑий клип, Ñнова нажмиÑе Start. ÐоÑле ÑÑого в Ñазделе Lines поÑвиÑÑÑ Ð±Ð»Ð¾Ðº ÑооÑвеÑÑÑвÑÑÑего клипа.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"Ðнопка End ÑÑÐ°Ð²Ð¸Ñ Ð¼ÐµÑÐºÑ Ð² конÑе видео. ÐÑа меÑка ÑÑÐ°Ð½ÐµÑ ÐºÐ¾Ð½Ñом поÑледнего клипа.","How to assign characters to clips":"Ðак назнаÑиÑÑ Ð¿ÐµÑÑонажей клипам","Under Characters, enter the names of all characters used in the Dub Pack.":"Ð Ñазделе Characters ÑкажиÑе имена вÑеÑ
пеÑÑонажей, иÑполÑзÑемÑÑ
в Dub Pack.","Then go to Lines and select the corresponding character for each clip.":"ÐаÑем оÑкÑойÑе Lines и вÑбеÑиÑе ÑооÑвеÑÑÑвÑÑÑего пеÑÑонажа Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ клипа.","How to separate the voice and background audio":"Ðак оÑделиÑÑ Ð³Ð¾Ð»Ð¾Ñ Ð¾Ñ Ñонового звÑка","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"ÐажмиÑе Split voice & background, как показано ниже. ÐнÑÑÑÑÐ¼ÐµÐ½Ñ Ð°Ð²ÑомаÑиÑеÑки ÑÐ°Ð·Ð´ÐµÐ»Ð¸Ñ Ð³Ð¾Ð»Ð¾Ñ Ð¸ ÑоновÑй звÑк вÑего видео.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Шкала вÑемени Mod Maker Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°Ð¼Ð¸ Split voice & background, Start и End","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"ÐÑполÑзÑйÑе Split voice & background Ð´Ð»Ñ ÑÐ°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð·Ð²Ñка. ÐÑмеÑайÑе ÐºÐ»Ð¸Ð¿Ñ ÐºÐ½Ð¾Ð¿ÐºÐ°Ð¼Ð¸ Start и End.","How to set the mod title and author":"Ðак ÑказаÑÑ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ и авÑоÑа мода","Enter the mod title, author and other information under Mod setup.":"УкажиÑе название мода, авÑоÑа и дÑÑгÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð² Ñазделе Mod setup.","The complete process":"ÐолнÑй пÑоÑеÑÑ","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"ÐеÑеÑаÑиÑе MP4-Ñайл в Mod Maker. ÐажмиÑе Split voice & background, ÑÑÐ¾Ð±Ñ ÑазделиÑÑ Ð³Ð¾Ð»Ð¾Ñ Ð¸ ÑоновÑй звÑк. ÐÑполÑзÑйÑе Start Ð´Ð»Ñ ÑÐ°Ð·Ð´ÐµÐ»ÐµÐ½Ð¸Ñ Ð²Ð¸Ð´ÐµÐ¾ на клипÑ, а заÑем End, ÑÑÐ¾Ð±Ñ Ð¾ÑмеÑиÑÑ ÐºÐ¾Ð½ÐµÑ Ð¿Ð¾Ñледнего клипа.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"Ð Ñазделе Lines добавÑÑе к ÐºÐ°Ð¶Ð´Ð¾Ð¼Ñ ÐºÐ»Ð¸Ð¿Ñ ÑооÑвеÑÑÑвÑÑÑего пеÑÑонажа и ÑепликÑ. ÐаÑем заполниÑе оÑновнÑÑ Ð¸Ð½ÑоÑмаÑÐ¸Ñ Ð¾ моде в Ñазделе Mod setup.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"ÐаконеÑ, оÑкÑойÑе Validation & export, пÑовеÑÑÑе, не пÑопÑÑена ли какаÑ-либо инÑоÑмаÑиÑ, и ÑкÑпоÑÑиÑÑйÑе Dub Pack.","WORKSHOP UPLOAD":"ÐÐÐРУÐÐÐ Ð ÐÐСТÐРСÐУЮ","How to Upload a Mod to Workshop":"Ðак загÑÑзиÑÑ Ð¼Ð¾Ð´ в ÐаÑÑеÑÑкÑÑ","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Ðа главной ÑÑÑаниÑе игÑÑ Ð½Ð°Ð¶Ð¼Ð¸Ñе на Ñвой аваÑÐ°Ñ Ð² пÑавом веÑÑ
нем ÑÐ³Ð»Ñ Ð¸ вÑбеÑиÑе Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"СÑÑаниÑа Upload Mod в Creator Workshop Ñ ZIP-Ñайлом мода, даннÑми пÑбликаÑии и вÑбоÑом ÑзÑка","Upload Mod in Creator Workshop.":"Upload Mod в Creator Workshop.","Upload the ZIP":"ÐагÑÑзиÑе ZIP-Ñайл","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"ÐÑбеÑиÑе Dub Mod или Judge Mod, заÑем вÑбеÑиÑе ZIP-Ñайл мода на ÑÑÑÑойÑÑве.","Complete the Workshop listing":"ÐаполниÑе ÑÑÑаниÑÑ Ð¼Ð¾Ð´Ð°","Fill in the title, description and tags, then choose the language spoken in the mod.":"УкажиÑе название, опиÑание и Ñеги, заÑем вÑбеÑиÑе ÑзÑк, на коÑоÑом говоÑÑÑ Ð² моде.","Choose the correct language":"ÐÑбеÑиÑе пÑавилÑнÑй ÑзÑк","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"Ð
153ÑбеÑиÑе пÑавилÑнÑй ÑзÑк мода. ÐнаÑе игÑоки, коÑоÑÑм он мог Ð±Ñ Ð±ÑÑÑ Ð¸Ð½ÑеÑеÑен, могÑÑ ÐµÐ³Ð¾ пÑопÑÑÑиÑÑ.","If you selected the wrong language, open My Mods and edit the mod information.":"ÐÑли вÑбÑан непÑавилÑнÑй ÑзÑк, оÑкÑойÑе My Mods и измениÑе инÑоÑмаÑÐ¸Ñ Ð¾ моде.","Publish the mod":"ÐпÑбликÑйÑе мод","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"ÐодÑвеÑдиÑе, ÑÑо Ñ Ð²Ð°Ñ ÐµÑÑÑ Ð¿Ñава или ÑазÑеÑение на пÑбликаÑÐ¸Ñ ÑÑого конÑенÑа, заÑем нажмиÑе Publish Mod.","Edit the mod later":"ÐзмениÑе мод позже","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"ÐоÑле пÑбликаÑии оÑкÑойÑе My Mods, еÑли нÑжно измениÑÑ ÑÑÑаниÑÑ Ð¼Ð¾Ð´Ð° или замениÑÑ ZIP-Ñайл."},uk:{"Voxalike Guide":"ÐоÑÑбник Voxalike","Learn how to play Voxalike and make mods.":"ÐÑзнайÑеÑÑ, Ñк гÑаÑи Ñ Voxalike Ñа ÑÑвоÑÑваÑи моди.","PLAYER GUIDE":"ÐÐСÐÐÐÐÐ ÐÐЯ ÐÐ ÐÐЦÐÐ",Guide:"ÐоÑÑбник","All guides":"УÑÑ Ð¿Ð¾ÑÑбники","FOR DEVELOPERS":"ÐÐЯ Ð ÐÐÐ ÐÐÐÐÐÐÐ","Technical Documentation":"ТеÑ
нÑÑна докÑменÑаÑÑÑ","Dub Pack and Judge Pack format references.":"ÐовÑÐ´ÐºÐ¾Ð²Ñ Ð¼Ð°ÑеÑÑали Ñодо ÑоÑмаÑÑв Dub Pack Ñ Judge Pack.","HOW TO PLAY":"ЯРÐÐ ÐТÐ","Choose a Dub Pack and make the scene yours.":"ÐибеÑÑÑÑ Dub Pack Ñ Ð¾Ð·Ð²ÑÑÑе ÑÑÐµÐ½Ñ Ð¿Ð¾-ÑвоÑмÑ.","A Dub Pack is a scene split into lines for you to perform. Find one in Workshop, choose how you want to play, then replace each original line with your own voice. The Mods page is simply where you can see and manage the Dub Packs and Judge Mods saved in your browser.":"Dub Pack â Ñе ÑÑена, подÑлена на ÑеплÑки Ð´Ð»Ñ Ð²Ð°Ñого озвÑÑеннÑ. ÐнайдÑÑÑ Ð¿Ð°ÐºÐµÑ Ñ ÐайÑÑеÑнÑ, вибеÑÑÑÑ Ñежим гÑи Ñа замÑнÑÑÑ ÐºÐ¾Ð¶Ð½Ñ Ð¾ÑигÑналÑÐ½Ñ ÑеплÑÐºÑ Ð²Ð»Ð°Ñним голоÑом. Ðа ÑÑоÑÑнÑÑ Â«Ðоди» можна пеÑеглÑдаÑи й кеÑÑваÑи пакеÑами Dub Pack Ñ Ð¼Ð¾Ð´Ð°Ð¼Ð¸ Judge Mod, збеÑеженими Ñ Ð±ÑаÑзеÑÑ.","Open Workshop from the top navigation, choose a Dub Pack and select Subscribe. Then return to the PLAY home screen, open Mods and wait for the pack to finish installing in your browser. There is no separate file to download to your device. You can also import a local ZIP from Mods.":"ÐÑдкÑийÑе ÐайÑÑеÑÐ½Ñ Ñ Ð²ÐµÑÑ
нÑй панелÑ, вибеÑÑÑÑ Dub Pack Ñ Ð½Ð°ÑиÑнÑÑÑ Subscribe. ÐоÑÑм повеÑнÑÑÑÑÑ Ð½Ð° головний екÑан PLAY, вÑдкÑийÑе «Ðоди» й доÑекайÑеÑÑ Ð²ÑÑÐ°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð°ÐºÐµÑа Ñ Ð±ÑаÑзеÑÑ. ÐкÑемий Ñайл на пÑиÑÑÑÑй заванÑажÑваÑи не поÑÑÑбно. У ÑоздÑÐ»Ñ Â«Ðоди» Ñакож можна ÑмпоÑÑÑваÑи локалÑний ZIP-Ñайл.","Before your first game, open Settings and run Mic Check. Apply the result for better voice cleanup while recording.":"ÐеÑед пеÑÑÐ¾Ñ Ð³ÑÐ¾Ñ Ð²ÑдкÑийÑе налаÑÑÑÐ²Ð°Ð½Ð½Ñ Ñа запÑÑÑÑÑÑ Mic Check. ÐаÑÑоÑÑйÑе ÑезÑлÑÑаÑ, Ñоб покÑаÑиÑи оÑиÑÐµÐ½Ð½Ñ Ð³Ð¾Ð»Ð¾ÑÑ Ð¿Ñд ÑÐ°Ñ Ð·Ð°Ð¿Ð¸ÑÑ.","Open PLAY and choose Single Player or Multiplayer.":"ÐÑдкÑийÑе PLAY Ñ Ð²Ð¸Ð±ÐµÑÑÑÑ Ð¾Ð´Ð¸Ð½Ð¾ÑÐ½Ñ Ð°Ð±Ð¾ меÑÐµÐ¶ÐµÐ²Ñ Ð³ÑÑ.","For Single Player, select one of your installed Dub Packs and start the pack.":"РодиноÑнÑй гÑÑ Ð²Ð¸Ð±ÐµÑÑÑÑ Ð¾Ð´Ð¸Ð½ Ð·Ñ Ð²ÑÑановлениÑ
Dub Pack Ñ Ð·Ð°Ð¿ÑÑÑÑÑÑ Ð¹Ð¾Ð³Ð¾.","For Multiplayer, create or join a room with friends. The host chooses Co-op or PVP, selects a Dub Pack and starts the game.":"У меÑежевÑй гÑÑ ÑÑвоÑÑÑÑ ÐºÑмнаÑÑ Ð· дÑÑзÑми або пÑиÑднайÑеÑÑ Ð´Ð¾ неÑ. ÐедÑÑий вибиÑÐ°Ñ Co-op або PVP, обиÑÐ°Ñ Dub Pack Ñ Ð·Ð°Ð¿ÑÑÐºÐ°Ñ Ð³ÑÑ.","For each line, watch the scene and listen to the original. When you are ready, record your version before the line ends. You can replay or retry a take before continuing.":"ÐÐ»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ñ ÑеплÑки пеÑеглÑнÑÑе ÑÑÐµÐ½Ñ Ñа пÑоÑлÑÑ
айÑе оÑигÑнал. Ðоли бÑдеÑе гоÑовÑ, запиÑÑÑÑ ÑÐ²Ð¾Ñ Ð²ÐµÑÑÑÑ Ð´Ð¾ завеÑÑÐµÐ½Ð½Ñ ÑеплÑки. ÐеÑед пÑодовженнÑм Ð·Ð°Ð¿Ð¸Ñ Ð¼Ð¾Ð¶Ð½Ð° пÑоÑлÑÑ
аÑи або зÑобиÑи заново.","Complete the Dub Pack to watch your finished dub, see the results and download your video or audio.":"ÐавеÑÑÑÑÑ Dub Pack, Ñоб пеÑеглÑнÑÑи гоÑове озвÑÑеннÑ, побаÑиÑи ÑезÑлÑÑаÑи Ñа заванÑажиÑи вÑдео або аÑдÑо.","Voxalike main menu with the Mods button below Single Player and Multiplayer":"Ðоловне Ð¼ÐµÐ½Ñ Voxalike з ÐºÐ½Ð¾Ð¿ÐºÐ¾Ñ Â«Ðоди» пÑд одиноÑÐ½Ð¾Ñ Ñа меÑÐµÐ¶ÐµÐ²Ð¾Ñ Ð³ÑоÑ","Where do I manage my Dub Packs?":"Ðе кеÑÑваÑи моÑми Dub Pack?","Return to the PLAY home screen and select Mods. This is where you can check installations, manage subscribed Dub Packs and import local packs.":"ÐовеÑнÑÑÑÑÑ Ð½Ð° головний екÑан PLAY Ñ Ð²Ð¸Ð±ÐµÑÑÑÑ Â«Ðоди». ТÑÑ Ð¼Ð¾Ð¶Ð½Ð° пеÑевÑÑиÑи вÑÑановленнÑ, кеÑÑваÑи пÑдпиÑками Ð
153½Ð° Dub Pack Ñа ÑмпоÑÑÑваÑи локалÑÐ½Ñ Ð¿Ð°ÐºÐµÑи.","MAKE A DUB MOD":"СТÐÐÐ ÐÐÐЯ DUB-ÐÐÐÐ","How to Make a Dub Pack":"Як ÑÑвоÑиÑи Dub Pack","First, open the Mod Maker and drag an MP4 file into it, as shown below.":"СпоÑаÑÐºÑ Ð²ÑдкÑийÑе Mod Maker Ñ Ð¿ÐµÑеÑÑгнÑÑÑ Ñ Ð½Ñого MP4-Ñайл, Ñк показано нижÑе.","This is the Dub Mod Maker Tool â":"ÐнÑÑÑÑÐ¼ÐµÐ½Ñ Dub Mod Maker знаÑ
одиÑÑÑÑ ÑÑÑ â","Created by Brunoxx.":"СÑвоÑено Brunoxx.","Technical documentation":"ТеÑ
нÑÑна докÑменÑаÑÑÑ","For the Dub Pack structure and technical details â":"СÑÑÑкÑÑÑа Dub Pack Ñ ÑеÑ
нÑÑÐ½Ñ Ð¿Ð¾Ð´ÑобиÑÑ â","Mod Maker Media and timeline page showing where to drag an MP4 file":"СÑоÑÑнка Media and timeline Ñ Mod Maker з облаÑÑÑ Ð´Ð»Ñ Ð¿ÐµÑеÑÑгÑÐ²Ð°Ð½Ð½Ñ MP4-ÑайлÑ","Drag an MP4 file into the Mod Maker.":"ÐеÑеÑÑгнÑÑÑ MP4-Ñайл Ñ Mod Maker.","Next, we need to split the video into clips, then add a character and the corresponding dialogue to each clip.":"ÐÐ°Ð»Ñ Ð¿Ð¾ÑÑÑбно подÑлиÑи вÑдео на клÑпи й додаÑи до кожного клÑÐ¿Ñ Ð¿ÐµÑÑонажа Ñа вÑдповÑÐ´Ð½Ñ ÑеплÑкÑ.","How to split the video into clips":"Як подÑлиÑи вÑдео на клÑпи","The Start button is used to place markers. The first marker becomes the beginning of the first clip.":"Ðнопка Start вÑÑановлÑÑ Ð¿Ð¾Ð·Ð½Ð°Ñки. ÐеÑÑа познаÑка ÑÑане поÑаÑком пеÑÑого клÑпÑ.","To end the current clip, press Start again. A section for the corresponding clip will then appear under Lines.":"Щоб завеÑÑиÑи поÑоÑний клÑп, Ð·Ð½Ð¾Ð²Ñ Ð½Ð°ÑиÑнÑÑÑ Start. ÐÑÑÐ»Ñ ÑÑого в ÑоздÑÐ»Ñ Lines зâÑвиÑÑÑÑ Ð±Ð»Ð¾Ðº вÑдповÑдного клÑпÑ.","The End button places a marker at the end of the video. This marker becomes the end of the final clip.":"Ðнопка End вÑÑановлÑÑ Ð¿Ð¾Ð·Ð½Ð°ÑÐºÑ Ð½Ð°Ð¿ÑикÑнÑÑ Ð²Ñдео. Ð¦Ñ Ð¿Ð¾Ð·Ð½Ð°Ñка ÑÑане кÑнÑем оÑÑаннÑого клÑпÑ.","How to assign characters to clips":"Як пÑизнаÑиÑи пеÑÑонажÑв клÑпам","Under Characters, enter the names of all characters used in the Dub Pack.":"У ÑоздÑÐ»Ñ Characters введÑÑÑ Ñмена вÑÑÑ
пеÑÑонажÑв, викоÑиÑÑаниÑ
Ñ Dub Pack.","Then go to Lines and select the corresponding character for each clip.":"ÐоÑÑм вÑдкÑийÑе Lines Ñ Ð²Ð¸Ð±ÐµÑÑÑÑ Ð²ÑдповÑдного пеÑÑонажа Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ клÑпÑ.","How to separate the voice and background audio":"Як вÑдокÑемиÑи Ð³Ð¾Ð»Ð¾Ñ Ð²Ñд Ñонового звÑкÑ","Click Split voice & background, as shown below. The tool will automatically separate the voice and background audio for the entire video.":"ÐаÑиÑнÑÑÑ Split voice & background, Ñк показано нижÑе. ÐнÑÑÑÑÐ¼ÐµÐ½Ñ Ð°Ð²ÑомаÑиÑно ÑоздÑлиÑÑ Ð³Ð¾Ð»Ð¾Ñ Ñ Ñоновий звÑк ÑÑÑого вÑдео.","Mod Maker timeline showing Split voice and background, Start and End buttons":"Шкала ÑаÑÑ Mod Maker Ñз кнопками Split voice & background, Start Ñ End","Use Split voice & background to separate the audio. Use Start and End to mark the clips.":"ÐикоÑиÑÑовÑйÑе Split voice & background Ð´Ð»Ñ ÑоздÑÐ»ÐµÐ½Ð½Ñ Ð·Ð²ÑкÑ. ÐознаÑайÑе клÑпи кнопками Start Ñ End.","How to set the mod title and author":"Як ÑказаÑи Ð½Ð°Ð·Ð²Ñ Ð¹ авÑоÑа мода","Enter the mod title, author and other information under Mod setup.":"УкажÑÑÑ Ð½Ð°Ð·Ð²Ñ Ð¼Ð¾Ð´Ð°, авÑоÑа Ñа ÑнÑÑ ÑнÑоÑмаÑÑÑ Ð² ÑоздÑÐ»Ñ Mod setup.","The complete process":"Ðовний пÑоÑеÑ","Drag an MP4 file into the Mod Maker. Click Split voice & background to separate the voice and background audio. Use Start to split the video into clips, then use End to mark the end of the final clip.":"ÐеÑеÑÑгнÑÑÑ MP4-Ñайл Ñ Mod Maker. ÐаÑиÑнÑÑÑ Split voice & background, Ñоб ÑоздÑлиÑи Ð³Ð¾Ð»Ð¾Ñ Ñ Ñоновий звÑк. ÐикоÑиÑÑовÑйÑе Start Ð´Ð»Ñ Ð¿Ð¾Ð´ÑÐ»Ñ Ð²Ñдео на клÑпи, а поÑÑм End, Ñоб познаÑиÑи кÑнеÑÑ Ð¾ÑÑаннÑого клÑпÑ.","Under Lines, add the corresponding character and dialogue to each clip. Then complete the basic mod information under Mod setup.":"У ÑоздÑÐ»Ñ Lines додайÑе до Ð
153ºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ клÑÐ¿Ñ Ð²ÑдповÑдного пеÑÑонажа Ñа ÑеплÑкÑ. ÐоÑÑм заповнÑÑÑ Ð¾ÑÐ½Ð¾Ð²Ð½Ñ ÑнÑоÑмаÑÑÑ Ð¿Ñо мод Ñ ÑоздÑÐ»Ñ Mod setup.","Finally, open Validation & export, check whether any information is missing, and export the Dub Pack.":"ÐаÑамкÑнеÑÑ Ð²ÑдкÑийÑе Validation & export, пеÑевÑÑÑе, Ñи не пÑопÑÑено ÑкÑÑÑ ÑнÑоÑмаÑÑÑ, Ñа екÑпоÑÑÑйÑе Dub Pack.","WORKSHOP UPLOAD":"ÐÐÐÐÐТÐÐÐÐÐЯ ÐÐ ÐÐÐСТÐÐ ÐÐ","How to Upload a Mod to Workshop":"Як заванÑажиÑи мод до ÐайÑÑеÑнÑ","On the game home page, click your avatar in the upper-right corner, then select Creator Workshop.":"Ðа головнÑй ÑÑоÑÑнÑÑ Ð³Ñи наÑиÑнÑÑÑ ÑвÑй аваÑÐ°Ñ Ñ Ð¿ÑÐ°Ð²Ð¾Ð¼Ñ Ð²ÐµÑÑ
нÑÐ¾Ð¼Ñ ÐºÑÑÑ Ñа вибеÑÑÑÑ Creator Workshop.","Creator Workshop Upload Mod page with the mod ZIP, listing details and language fields":"СÑоÑÑнка Upload Mod Ñ Creator Workshop Ñз ZIP-Ñайлом мода, даними пÑблÑкаÑÑÑ Ñа вибоÑом мови","Upload Mod in Creator Workshop.":"Upload Mod Ñ Creator Workshop.","Upload the ZIP":"ÐаванÑажÑе ZIP-Ñайл","Choose Dub Mod or Judge Mod, then select the mod ZIP from your device.":"ÐибеÑÑÑÑ Dub Mod або Judge Mod, а поÑÑм вибеÑÑÑÑ ZIP-Ñайл мода на пÑиÑÑÑоÑ.","Complete the Workshop listing":"ÐаповнÑÑÑ ÑÑоÑÑÐ½ÐºÑ Ð¼Ð¾Ð´Ð°","Fill in the title, description and tags, then choose the language spoken in the mod.":"УкажÑÑÑ Ð½Ð°Ð·Ð²Ñ, Ð¾Ð¿Ð¸Ñ Ñ Ñеги, а поÑÑм вибеÑÑÑÑ Ð¼Ð¾Ð²Ñ, ÑÐºÐ¾Ñ Ð³Ð¾Ð²Ð¾ÑÑÑÑ Ñ Ð¼Ð¾Ð´Ñ.","Choose the correct language":"ÐибеÑÑÑÑ Ð¿ÑавилÑÐ½Ñ Ð¼Ð¾Ð²Ñ","Choose the correct language for your mod. Otherwise, players who want to play the mod may ignore it.":"ÐибеÑÑÑÑ Ð¿ÑавилÑÐ½Ñ Ð¼Ð¾Ð²Ñ Ð¼Ð¾Ð´Ð°. ÐнакÑе гÑавÑÑ, Ñким вÑн мÑг би бÑÑи ÑÑкавий, можÑÑÑ Ð¹Ð¾Ð³Ð¾ пÑопÑÑÑиÑи.","If you selected the wrong language, open My Mods and edit the mod information.":"ЯкÑо вибÑано непÑавилÑÐ½Ñ Ð¼Ð¾Ð²Ñ, вÑдкÑийÑе My Mods Ñ Ð·Ð¼ÑнÑÑÑ ÑнÑоÑмаÑÑÑ Ð¿Ñо мод.","Publish the mod":"ÐпÑблÑкÑйÑе мод","Confirm that you have the rights or permission to share the content, then select Publish Mod.":"ÐÑдÑвеÑдÑÑе, Ñо маÑÑе пÑава або дозвÑл на пÑблÑкаÑÑÑ ÑÑого вмÑÑÑÑ, а поÑÑм наÑиÑнÑÑÑ Publish Mod.","Edit the mod later":"ÐмÑнÑÑÑ Ð¼Ð¾Ð´ пÑзнÑÑе","After publishing, open My Mods if you need to edit the listing or replace the ZIP.":"ÐÑÑÐ»Ñ Ð¿ÑблÑкаÑÑÑ Ð²ÑдкÑийÑе My Mods, ÑкÑо поÑÑÑбно змÑниÑи ÑÑоÑÑÐ½ÐºÑ Ð¼Ð¾Ð´Ð° або замÑниÑи ZIP-Ñайл."}};function Jv(e,o){return e==="en"?o:ZE[e][o]??o}const _v={seoTitle:"Voxalike - Play Dub Game Online Free (Official)",description:"Voxalike is a browser dub game where you record character lines, compare your performance, play with friends, and download community dub mods from the Workshop."},XE=[{label:"Version v1.5.5",title:"Voxalike v1.5.5 Supporter gifting and multiplayer update",changes:["Feature: You can now send Supporter directly to another player using the email address linked to their Voxalike account. Your own linked email is available under Account details.","Feature: You can also purchase Supporter gift codes. After payment, find the issued codes on the Support page. To redeem one, open your avatar menu in the top-right corner, choose Redeem code, and enter the code.","Feature: Players can now use text chat while playing a Multiplayer game. The in-game chat also shows the current room population and announces when a player leaves.","Feature: Player profiles can now show multiplayer records, including completed Co-op and PvP games and average scores. You can hide these stats from other players under Account details; they remain visible to you.","Feature: Workshop mod detail pages n
153ow show which lines belong to each character.","Improvement: Improved the waveform display update rate for smoother audio level feedback while recording.","Bug fix: Fixed Role Claim games with more than 24 characters being unable to continue, and improved the Role Claim selection panel so large character lists are easier to browse."],date:"2026.09.25"},{label:"Version v1.5.4",title:"Voxalike v1.5.4 Role Claim and results update",changes:["Feature: Added Role Claim mode to Co-op. Choose a character before dubbing, then record that character's lines.","Improvement: The Final Score screen has a cleaner game-style layout, compact share buttons, and a way to report incorrect mod roles or language.","Improvement: Video downloads show which formats are ready, rendering, or waiting. You can download a ready video while another renders, or tap a waiting format to switch to it.","Improvement: iPhone and iPad exports now offer a tap-to-continue prompt when Safari blocks video playback and clearer help when a mod video cannot be played.","Improvement: Workshop warns creators when an MP4 uses video or audio that may not work on iPhone Safari and asks for H.264/AAC instead."],date:"2026.09.21"},{label:"Version v1.5.3",title:"Voxalike v1.5.3 Supporter and profile update",changes:["Feature: Support Voxalike with a one-time US$5 purchase to unlock an ad-free account, an animated Supporter badge, six name colors and three avatar frames.","Feature: Customize your name color and avatar frame in your profile. Supporter styles also appear in multiplayer rooms and player chat.","Improvement: Refreshed player profiles, room player cards and site navigation with clearer avatars and a more consistent layout.","Improvement: Co-op take previews now skip unnecessary uploads when no other player is connected and use fewer room writes, reducing server usage without changing the listening experience."],date:"2026.09.19"},{label:"Version v1.5.2",title:"Voxalike v1.5.2 Co-op quality update",changes:["Feature: After a player records a line in Co-op, the other players can listen to the latest take while they wait. The recording is only submitted and the room only advances when the recording player chooses Next.","Improvement: Video exports now use a consistent high-quality audio profile with final mix protection, plus compatibility fallbacks for iPhone, iPad and PC browsers."],date:"2026.09.17"},{label:"Version v1.5.1",title:"Voxalike v1.5.1 safety and Workshop update",changes:["Safety: Added harmful-content filtering for player names, public room titles and room chat.","Feature: Added a player reporting system for public multiplayer rooms.","Improvement: Mod uploads now provide clearer validation and specific feedback when an upload cannot be published."],date:"2026.09.04"},{label:"Version v1.5.0",title:"Voxalike v1.5.0 multiplayer and creator update",changes:["Feature: Added the Public Multiplayer Lobby with language and waiting-room filters, while private rooms remain protected by their room code.","Feature: Multiplayer rooms now include text chat, player avatars, profile links and automatic host transfer when everyone is ready but the host does not start the game.","Feature: Co-op Claim Mode hosts can export a Recovery ZIP and import it later to restore completed recordings and continue the session in a new room.","Feature: Workshop Dub Mods now have Community Highlights, while creator profiles can display the Mod Creator badge and an average rating after enough ratings are received.","Improvement: Added a microphone volume control and improved finished-video exports for longer Dub Mods."],date:"2026.08.28"},{label:"Version v1.4.7",title:"Voxalike v1.4.7 profile and Workshop update",changes:["Feature: Redesigned public player profiles and profile editing, including avatars, badges, creator statistics, published mods and uploader links from mod pages.","Feature: Expanded multilingual support across Workshop browsing, mod pages and player profiles.","Feature: Mod pages now show a factual package summary when the uploader did not provide a description.","Change: New uploads require titles of at least 5 characters and descriptions of at least 100 characters for Dub Mods or 60 characters for Judge Mods. Existing mods are unchanged."],date:"2026.08.22"},{label:"Version v1.4.6",title:"Voxalike v1.4.6 Workshop and recording update",changes:["Feature: Replacing a mod ZIP now requires an update note, shown on the mod's separate Change Notes page.","Feature: Added Workshop filters for language and role count. Search also supports multiple tags separated by commas and only shows mods matching every tag.","Feature: Players can now calibrate recording delay in Settings for their microphone setup."],date:"2026.08.21"},{label:"Version v1.4.5",title:"Voxalike v1.4.5 Workshop update",changes:["Feature: Added multilingual support to Creator Workshop.","Feature: Optimized D1 database performance.","Feature: Added clear feedback when a mod upload fails."],date:"2026.08.19"},{label:"Version v1.4.4",title:"Voxalike v1.4.4 stability and Workshop update",changes:["bug: Fixed video export issues. Most devices can now successfully export their creations, including videos recovered through Recent Dubs.","bug: Fixed an issue where non-host players could be asked to download the Dub Pack again after reconnecting.","Feature: Optimized the Workshop experience for smoother browsing and added Trending This Week.","Feature: Workshop cards now display each pack's language."],date:"2026.08.18"},{label:"Version v1.4.3",title:"Voxalike v1.4.3 Workshop and local mod update",changes:["Added Trending This Week as the default Workshop sort for Dub Mods and Judge Mods, balancing activity from the most recent seven days with a launch boost that fades over a mod's first 14 days. Workshop sorting and pagination now update in place for faster browsing.","Improved Multiplayer audio loading by securely caching repeated room audio closer to players.","bug: Microphone cleanup could make voices sound muffled or cut off quiet word endings. Natural mode now preserves more voice detail by default, while the optional Clean voice mode provides stronger noise reduction for noisy rooms.","bug: Browser video export could remain stuck forever and lock the download buttons when background rendering, media recording, seeking, or WebM finalization stopped responding. Export stages now time out safely, retry recoverable failures, preserve flushed video data when possible, and let players cancel background preparation before starting a fresh download.","bug: Reconnecting players could be asked to install a Workshop Dub Mod that was already in their local library. Room downloads now subscribe automatically, and legacy installs are recognized and linked to their Workshop entry.","bug: Importing an original ChoicerVoicer or GameBanana pack with OGV video could be rejected without leaving a visible explanation. Voxalike now keeps a clear conversion notice on screen.","bug: Multiplayer hosts could not see the roles included in their selected Dub Mod. The room panel now shows a compact role summary without allowing long character lists to break the layout."],date:"2026.08.17"},{label:"Version v1.4.2",title:"Voxalike v1.4.2 multilingual and bug fix update",changes:["Added multilingual interface support for English, German, Russian, and Ukrainian.","bug: Some players could be redirected out of an active Multiplayer game and then be unable to re
153connect to the same room.","bug: On some browsers, pressing Skip during Dub Premiere could stop finished-video rendering before it completed.","bug: On some browsers, Single Player could remain on Preparing Stage and never enter the game because imported clip audio could not be read from its temporary Blob URL.","bug: Finished video export could remain frozen when a browser's video decoder stopped producing new frames.","bug: iPhone and iPad could finish preparing a video but fail to open a usable save or download action.","bug: Browser translation tools could interrupt the page after modifying game interface text.","bug: Temporary database latency could delay or time out Single Player session, round, and score requests."],date:"2026.08.17"},{label:"Version v1.4.1",title:"Voxalike v1.4.1 quality-of-life update",changes:["Added Parallel Race as an optional PVP format: every player records the same full clip list, and the final ranking rewards both accuracy and completion time.","Rebalanced final scores so average performances cluster less tightly and excellent performances stand out more clearly.","Upgraded multiplayer recording transfer quality while preserving compatibility with mobile devices.","Added background-track playback to My Take and finished exports, with one simple Backgroun
153d volume control.","Improved recording review so live recording and My Take playback share the same waveform timeline while retaining the get-ready and ending margins.","Improved Co-op progression and multiplayer mod syncing, including mods whose titles contain accidental extra spacing.","Workshop subscriptions now receive replacement mod files automatically and remove mods that are no longer available.","Refined high-resolution desktop layouts, Mic Check status, and loading, playback, room setup, and empty-library feedback."],emphasis:"If a room becomes stuck while syncing a mod, refresh the page to reconnect and let Voxalike check the room mod again.",date:"2026.08.15"},{label:"Version v1.4.0",title:"Voxalike v1.4.0 stage and Workshop update",body:"This major update redesigns the Voxalike stage and main menu with new artwork, controls, subtitles, judge presentation, speech bubbles, reaction audio, background music, and improved audio settings. Solo and Co-op players can optionally hear the original line at low volume while recording, while Co-op hosts can skip an unplayable line without leaving a silent gap in the finished dub. Multiplayer now keeps recent completed dubs available from the Create or Join Room screen, making it easier to return to a result and download the finished video after the host returns to the lobby. Video exporting is also more reliable, with automatic startup retries, clearer progress stages, and a Cancel & Retry option. The Creator Workshop now combines uploads and My Mods into one workspace with listing edits, ZIP replacement, mod hiding, delayed permanent deletion, replacement links, duplicate cleanup, clearer upload rules, and smoother page loading. Judge Mod artwork and Workshop cards also use more consistent sizing. Accounts can now be permanently deleted from Edit Profile.",emphasis:"Recent Dubs lets players return to recent multiplayer results and download the finished video after the room returns to the lobby.",date:"2026.08.14"},{label:"Version v1.3.2",title:"Voxalike v1.3.2 multiplayer and recording update",body:"This update expands Co-op rooms to 12 players while keeping PVP at 8 players for a manageable scoring flow. Mobile room layouts are now more compact so larger groups remain clear and usable on phones and tablets. iPhone and iPad recording is also more reliable: Voxalike can recover a take when iOS records valid microphone audio but fails to decode its own audio file, and improved diagnostics make microphone or take-processing failures easier to report.",emphasis:"If a mobile recording still fails, use the diagnostic copy button and include the report with your bug report.",date:"2026.08.12"},{label:"Version v1.3.1",title:"Voxalike v1.3.1 compatibility update",body:"This update improves microphone startup and recording reliability on mobile browsers, especially after switching apps or returning to a game. Safari can now decode more OGG audio used by local Dub Mods. Single Player also adds a Quick Jump control: players can move past one or several unwanted clips while keeping their original audio in the finished video. Skipped clips are not scored. Workshop pages are more compact and easier to browse, upload validation now catches clips longer than 60 seconds before publishing, and the language selector no longer remains stuck on a previous choice.",emphasis:"If microphone access still fails on mobile, check that the page is using a secure connection and temporarily disable VPN or browser security tools that interfere with HTTPS.",date:"2026.08.11"},{label:"Version v1.3.0",title:"Voxalike v1.3.0 mobile update",body:"This major update makes Voxalike much easier to play on phones, tablets, and Chromebooks. Mobile landscape layouts have been rebuilt across Single Player, Multiplayer rooms, Mods, Settings, recording, Dub Premiere, and results, with more compact controls, clearer player states, and fewer overlapping elements. Voxalike can now be installed as an app on supported mobile browsers, and Workshop pages remain available inside the installed experience. This update also improves mobile Mic Check compatibility, Workshop mod management, automatic room mod installation feedback, and Top Rated sorting.",emphasis:"Mobile support is still being improved. If you find a mobile UI bug or have a suggestion, please post it in the matching Discord channel and include a screenshot when possible.",date:"2026.08.11"},{label:"Version v1.2.6",title:"Voxalike v1.2.6 stability update",body:"This small update improves microphone reliability during rounds. Voxalike now detects disconnected microphone streams before recording, reconnects when needed, and stops interrupted takes with a clear retry message instead of silently saving missing audio. Mic Check is also more resistant to brief noise spikes, and quiet voices are less likely to be removed by an inaccurate calibration.",date:"2026.08.10"},{label:"Version v1.2.5",title:"Voxalike v1.2.5 update",body:"This update introduces the C
153ommunity Contributor program, with Ad-Free access, an exclusive multiplayer badge, and application notifications for approved contributors. Workshop creators can now use a unified upload page and edit their Dub Mod or Judge Mod listing details after publishing. Multiplayer now uses account display names for signed-in players and generated names for guests. Host Tools are easier to find, and long Clip Scores lists can now be scrolled correctly.",date:"2026.08.09"},{label:"Version v1.2.4",title:"Voxalike v1.2.4 update",body:"This update adds Session Recovery to help players continue after a refresh, browser crash, or interrupted session. Single Player can restore the most recent interrupted run, while multiplayer reconnects can recover locally saved takes alongside the room state. Co-op now also has a clearer Round Robin or Claim Mode selector, with an optional Randomize Player Order setting for Round Robin games.",date:"2026.08.08"},{label:"Version v1.2.3",title:"Voxalike v1.2.3 update",body:"This update rebuilds the multiplayer room system to improve reliability during long sessions and periods with more active players. It also improves the Mods experience with automatic installation after subscribing, unified searchable mod lists, clear Dub Mod and Judge Mod labels, and a simpler import menu. Host recovery controls are now tucked into a clearer emergency menu, and exported WebM videos now include improved seeking metadata for better timeline scrubbing in compatible video players.",date:"2026.08.08"},{label:"Version v1.2.2",title:"Voxalike v1.2.2 stability update",body:"This update improves multiplayer room recovery and long-session stability. Rooms now last longer, players can restore their room after refreshing in the same browser, and temporary server overloads are handled more safely during recording or saving. Co-op Claim Mode also now lets hosts release a stuck claimed line, so a disconnected player is less likely to block the whole room.",date:"2026.08.08"},{label:"Version v1.2.1",title:"Voxalike v1.2.1 update",body:"This small update improves Co-op Claim Mode by letting players release a line they accidentally took before saving the recording. Once released, the line becomes open again for another player to claim.",date:"2026.08.08"},{label:"Version v1.2.0",title:"Voxalike v1.2.0 update",body:"This update makes Judge Mods a full Workshop mod type with upload, download, subscription, detail pages, ratings, and sorting. Judge Mods can replace the four judges with custom art, names, score labels, feedback lines, and single or score-tier reaction sounds. The final results screen now shows the active judges with their feedback, and the Guide now includes clearer articles for making Dub Mods and Judge Mods.",links:[{label:"â¡ï¸ Judge Mod Guide",href:"/docs/judge-pack-format"}],date:"2026.08.07"},{label:"Version v1.1.6",title:"Voxalike v1.1.6 update",body:"This small update adds a short get-ready window before recording starts, making it easier to enter a line on time. It also improves score saving reliability during longer runs.",date:"2026.08.05"},{label:"Version v1.1.5",title:"Voxalike v1.1.5 update",body:"This update improves the scoring algorithm, refreshes final result rating tiers, and shows judge feedback as readable performance notes instead of detailed sub-scores.",date:"2026.08.05"},{label:"Version v1.1.4",title:"Voxalike v1.1.4 update",body:"This small update improves multiplayer mod matching so rooms are less likely to load the wrong same-name dub pack. Workshop now includes a Most Characters sort option, and the mod guide explains how dub_characters helps Workshop count roles accurately.",date:"2026.08.05"},{label:"Version v1.1.3",title:"Voxalike v1.1.3 update",body:"This update improves dubbed video export reliability, adds clearer WebM export guidance, and makes My Take playback feel more natural by replaying your recorded line with the clip video. Local mod importing now warns when a pack uses OGV video, since OGV can cause multiplayer dubbed playback or export issues. MP4 video is recommended for local mods.",date:"2026.08.05"},{label:"Version v1.1.2",title:"Voxalike v1.1.2 update",body:"Fixed an issue where some differently structured dub packs could treat a full audio track as an extra final clip, causing the last clip to replay the whole scene.",date:"2026.08.05"},{label:"Community",title:"Official recommended Dub Mods Maker",body:"Voxalike now has an official recommended Dub Mods Maker for creating dub packs. Thanks to community member Brnoxx for building the tool.",links:[{label:"â¡ï¸ Dub Mods Maker",href:"https://app.voxalike-mod-maker.workers.dev/"}],date:"2026.08.04"},{label:"Version v1.1.1",title:"Voxalike v1.1.1 update",body:"This update improves microphone noise cleanup with adaptive per-clip processing, adds a clearer Mic Check flow in Settings, and improves network timeout handling across solo and multiplayer.",emp
153hasis:"For the best voice cleanup, run Mic Check and apply it before playing.",date:"2026.08.04"},{label:"Version v1.1.0",title:"Voxalike v1.1.0 update",body:"This update expands multiplayer rooms to 8 players, adds Co-op Claim Mode, optional multiplayer re-records, cleaner recording controls, My Take playback feedback, PVP safeguards for large mods, and Clean or Subtitles export options. Thanks to community member Kiriufa for the suggestions that shaped this update.",date:"2026.08.04"},{label:"Version v1.0.2",title:"Voxalike v1.0.2 update",body:"This update improves browser video export stability and fixes a small Single Player UI issue where long mod lists could not scroll correctly.",date:"2026.08.04"},{label:"Version v1.0.1",title:"Voxalike v1.0.1 update",body:"The August 3 update improves result sharing with vertical Shorts exports, TikTok and YouTube upload shortcuts, clearer download and share actions, faster background video preparation, better noise handling, smoother multiplayer room flow, and Workshop pagination.",date:"2026.08.03"},{label:"Version v1.0",title:"Voxalike v1.0 is live",body:"Voxalike v1.0 brings playable dub mods, solo stage runs, score judging, dubbed video playback and downloads, invite-only multiplayer rooms, co-op and PVP modes, Workshop browsing, subscriptions, account login, local mod import, and microphone setup.",date:"2026.08.02"}],Ph={seoTitle:"Voxalike News - Updates and Patch Notes",description:"Read the latest Voxalike updates, patch notes, multiplayer changes, mic cleanup improvements, and dub game feature releases.",items:XE},QE={fr:{"Version v1.5.5":{label:"Version v1.5.5",title:"Voxalike v1.5.5 â cadeaux Supporter et mise à jour multijoueur",changes:["Fonctionnalité : Vous pouvez désormais offrir Supporter directement à un autre joueur à lâaide de lâadresse e-mail associée à son compte Voxalike. Vous trouverez votre propre adresse associée dans « Détails du compte ».","Fonctionnalité : Vous pouvez également acheter des codes cadeaux Supporter. Après le paiement, les codes attribués apparaissent sur la page Support. Pour en utiliser un, ouvrez le menu de votre avatar en haut à droite, sélectionnez « Utiliser un code », puis saisissez le code.","Fonctionnalité : Les joueurs peuvent désormais discuter par écrit pendant une partie multijoueur. Le chat en jeu affiche aussi le nombre de joueurs présents dans la salle et signale lorsquâun joueur quitte la partie.","Fonctionnalité : Les profils peuvent désormais afficher les statistiques multijoueur, notamment le nombre de parties Co-op et PvP terminées et les scores moyens. Vous pouvez masquer ces statistiques aux autres joueurs dans « Détails du compte » ; elles restent visibles pour vous.","Fonctionnalité : Les pages de détails des mods du Workshop indiquent désormais les répliques attribuées à chaque personnage.","Amélioration : La fréquence dâactualisation de la forme dâonde a été optimisée pour afficher le niveau audio plus fluidement pendant lâenregistrement.","Correction : Les parties en sélection libre des rôles peuvent maintenant démarrer avec plus de 24 personnages. Le panneau de sélection a également été amélioré pour faciliter la navigation dans les longues listes."]}},it:{"Version v1.5.5":{label:"Versione v1.5.5",title:"Voxalike v1.5.5 â regali Supporter e aggiornamento multigiocatore",changes:["Novità : ora puoi regalare Supporter direttamente a un altro giocatore usando lâindirizzo e-mail associato al suo account Voxalike. Puoi trovare il tuo indirizzo associato in «Dettagli dellâaccount».","Novità : puoi anche acquistare codici regalo Supporter. Dopo il pagamento, i codici assegnati compaiono nella pagina Support. Per usarne uno, apri il menu del tuo avatar in alto a destra, seleziona «Riscatta codice» e inserisci il codice.","Novità : ora i giocatori possono usare la chat di testo durante una partita multigiocatore. La chat di gioco mostra anche il numero di giocatori presenti nella stanza e segnala quando qualcuno abbandona la partita.","Novità : i profili possono ora mostrare le statistiche multigiocatore, incluse le partite Co-op e PvP completate e i punteggi medi. Puoi nascondere queste statistiche agli altri giocatori in «Dettagli dellâaccount»; resteranno comunque visibili a te.","Novità : le pagine dei dettagli dei mod del Workshop mostrano ora quali battute appartengono a ciascun personaggio.","Miglioramento: è stata ottimizzata la frequenza di aggiornamento della forma dâonda per mostrare il livello audio in modo più fluido durante la registrazione.","Correzione: le partite con selezione libera dei ruoli possono ora iniziare con più di 24 personaggi. à stato inoltre migliorato
153il pannello di selezione per rendere più semplice consultare gli elenchi lunghi."]}},es:{"Version v1.5.5":{label:"Versión v1.5.5",title:"Voxalike v1.5.5 â regalos Supporter y actualización multijugador",changes:["Función: ahora puedes regalar Supporter directamente a otro jugador mediante el correo electrónico vinculado a su cuenta de Voxalike. Puedes consultar tu propio correo vinculado en «Detalles de la cuenta».","Función: también puedes comprar códigos de regalo Supporter. Después del pago, los códigos asignados aparecerán en la página Support. Para usar uno, abre el menú de tu avatar en la esquina superior derecha, selecciona «Canjear código» e introduce el código.","Función: ahora los jugadores pueden usar el chat de texto durante una partida multijugador. El chat también muestra cuántos jugadores hay en la sala y avisa cuando alguien abandona la partida.","Función: los perfiles ahora pueden mostrar estadÃsticas multijugador, incluidas las partidas de Co-op y PvP completadas y las puntuaciones medias. Puedes ocultar estas estadÃsticas a otros jugadores desde «Detalles de la cuenta»; tú seguirás viéndolas.","Función: las páginas de detalles de los mods del Workshop muestran ahora qué frases pertenecen a cada personaje.","Mejora: se ha optimizado la frecuencia de actualización de la forma de onda para mostrar el nivel de audio con mayor fluidez durante la grabación.","Corrección: las partidas con selección libre de roles ahora pueden empezar con más de 24 personajes. También se ha mejorado el panel de selección para facilitar la navegación por listas largas."]}},pt:{"Version v1.5.5":{label:"Versão v1.5.5",title:"Voxalike v1.5.5 â ofertas Supporter e atualização multijogador",changes:["Funcionalidade: agora pode oferecer Supporter diretamente a outro jogador através do endereço de correio eletrónico associado à respetiva conta Voxalike. Pode consultar o seu próprio endereço associado em «Detalhes da conta».","Funcionalidade: também pode comprar códigos de oferta Supporter. Após o pagamento, os códigos atribuÃdos aparecem na página Support. Para utilizar um, abra o menu do avatar no canto superior direito, selecione «Resgatar código» e introduza o código.","Funcionalidade: os jogadores podem agora utilizar o chat de texto durante uma partida multijogador. O chat também mostra quantos jogadores estão na sala e avisa quando alguém sai da partida.","Funcionalidade: os perfis podem agora apresentar estatÃsticas multijogador, incluindo partidas Co-op e PvP concluÃdas e pontuações médias. Pode ocultar estas estatÃsticas dos outros jogadores em «Detalhes da conta»; continuarão visÃveis para si.","Funcionalidade: as páginas de detalhes dos mods do Workshop mostram agora que falas pertencem a cada personagem.","Melhoria: a frequência de atualização da forma de onda foi otimizada para apresentar o nÃvel de áudio de forma mais fluida durante a gravação.","Correção: as partidas com escolha livre de funções podem agora começar com mais de 24 personagens. O painel de seleção também foi melhorado para facilitar a navegação em listas extensas."]}}},eA={...QE,de:{"Version v1.5.5":{label:"Version v1.5.5",title:"Voxalike v1.5.5 â Supporter-Geschenke und Mehrspieler-Update",changes:["Funktion: Du kannst Supporter jetzt direkt an einen anderen Spieler verschenken. Verwende dazu die E-Mail-Adresse, die mit seinem Voxalike-Konto verknüpft ist. Deine eigene verknüpfte E-Mail-Adresse findest du unter âKontodetailsâ.","Funktion: Du kannst auÃerdem Supporter-Geschenkcodes kaufen. Nach der Zahlung findest du die ausgegebenen Codes auf der Support-Seite. Zum Einlösen öffnest du oben rechts dein Avatar-Menü, wählst âCode einlösenâ und gibst den Code ein.","Funktion: Spieler können jetzt während einer Mehrspielerpartie per Textchat schreiben. Der Chat im Spiel zeigt auÃerdem die aktuelle Spielerzahl im Raum an und meldet, wenn ein Spieler den Raum verlässt.","Funktion: Spielerprofile können jetzt Mehrspielerstatistiken anzeigen, darunter abgeschlossene Co-op- und PvP-Partien sowie Durchschnittswertungen. Unter âKontodetailsâ kannst du diese Werte für andere Spieler ausblenden; für dich selbst bleiben sie sichtbar.","Funktion: Auf den Detailseiten der Workshop-Mods wird jetzt angezeigt, welche Zeilen zu welcher Figur gehören.","Verbesserung: Die Aktualisierungsrate der Wellenformanzeige wurde verbessert, damit die Audiopegel während der Aufnahme flüssiger dargestellt werden.","Fehlerbehebung: Es wurde ein Problem behoben, durch das Partien im Modus âRollen beanspruchenâ mit mehr als 24 Figuren nicht fortgesetzt werden konnten. AuÃerdem wurde die Rollenauswahl verbessert, damit sich groÃe Figurenlisten leichter durchsuchen lassen."]}},nl:{"Version v1.5.5":{label:"Versie v1.5.5",title:"Voxalike v1.5.5 Supporter-cadeaus en multiplayerupdate",changes:["Functie: Je kunt Supporter nu rechtstreeks aan een andere speler cadeau doen via het e-mailadres dat aan diens Voxalike-account is gekoppeld. Je eigen gekoppelde e-mailadres vind je bij Accountgegevens.","Functie: Je kunt ook Supporter-cadeaucodes kopen. Na betaling vind je de uitgegeven codes op de Support-pagina. Om een code in te wisselen open je rechtsboven je avatarmenu, kies je âCode inwisselenâ en voer je de code in.","Functie: Spelers kunnen nu tekstchat gebruiken tijdens een multiplayerwedstrijd. De chat in het spel toont ook het huidige aantal spelers in de kamer en meldt wanneer een speler vertrekt.","Functie: Spelersprofielen kunnen nu multiplayerstatistieken tonen, waaronder voltooide Co-op- en PvP-wedstrijden en gemiddelde scores. Bij Accountgegevens kun je deze statistieken voor andere spelers verbergen; zelf blijf je ze zien.","Functie: Op de detailpagina van een Workshop-mod staat nu welke zinnen bij elk personage horen.","Verbetering: De verversingssnelheid van de golfvormweergave is verbeterd, zodat audioniveaus tijdens het opnemen vloeiender worden weergegeven.","Bugfix: Er is een probleem opgelost waardoor Role Claim-spellen met meer dan 24 personages niet verder konden. Ook is het Role Claim-keuzepaneel verbeterd, zodat grote lijsten met personages makkelijker te bekijken zijn."]}},ru:{"Version v1.5.5":{label:"ÐеÑÑÐ¸Ñ v1.5.5",title:"Voxalike v1.5.5 â подаÑки Supporter и обновление ÑеÑевой игÑÑ",changes:["ФÑнкÑиÑ: ТепеÑÑ Supporter можно подаÑиÑÑ Ð½Ð°Ð¿ÑÑмÑÑ Ð´ÑÑÐ³Ð¾Ð¼Ñ Ð¸Ð³ÑокÑ, иÑполÑзÑÑ Ð°Ð´ÑÐµÑ ÑлекÑÑонной поÑÑÑ, пÑивÑзаннÑй к его аккаÑнÑÑ Voxalike. ÐдÑеÑ, пÑивÑзаннÑй к ваÑÐµÐ¼Ñ Ð°ÐºÐºÐ°ÑнÑÑ, можно поÑмоÑÑеÑÑ Ð² Ñазделе «ÐаннÑе аккаÑнÑа».","ФÑнкÑиÑ: Также можно покÑпаÑÑ Ð¿Ð¾Ð´Ð°ÑоÑнÑе ÐºÐ¾Ð´Ñ Supporter. ÐоÑле оплаÑÑ Ð²ÑданнÑе ÐºÐ¾Ð´Ñ Ð¿Ð¾ÑвÑÑÑÑ Ð½Ð° ÑÑÑаниÑе поддеÑжки. ЧÑÐ¾Ð±Ñ Ð°ÐºÑивиÑоваÑÑ ÐºÐ¾Ð´, оÑкÑойÑе Ð¼ÐµÐ½Ñ Ð°Ð²Ð°ÑаÑа в пÑавом веÑÑ
нем ÑглÑ, вÑбеÑиÑе «ÐкÑивиÑоваÑÑ ÐºÐ¾Ð´Â» и введиÑе его.","ФÑнкÑиÑ: Ðо вÑÐµÐ¼Ñ ÑеÑевой игÑÑ ÑепеÑÑ Ð´Ð¾ÑÑÑпен ÑекÑÑовÑй ÑаÑ. ÐнÑÑÑиигÑовой ÑÐ°Ñ Ñакже показÑÐ²Ð°ÐµÑ ÑекÑÑее ÑиÑло игÑоков в комнаÑе и ÑообÑаеÑ, когда игÑок вÑÑ
одиÑ.","ФÑнкÑиÑ: РпÑоÑиле игÑока ÑепеÑÑ Ð¼Ð¾Ð¶ÐµÑ Ð¾ÑобÑажаÑÑÑÑ ÑÑаÑиÑÑика ÑеÑевой игÑÑ, вклÑÑÐ°Ñ ÑиÑло завеÑÑÑннÑÑ
маÑÑей Co-op и PvP и ÑÑедние оÑенки. Ð Ñазделе «ÐаннÑе аккаÑнÑа» ÑÑÑ ÑÑаÑиÑÑÐ¸ÐºÑ Ð¼Ð¾Ð¶Ð½Ð¾ ÑкÑÑÑÑ Ð¾Ñ Ð´ÑÑгиÑ
игÑоков; вам она оÑÑанеÑÑÑ Ð²Ð¸Ð´Ð½Ð°.","ФÑнкÑиÑ: Ðа ÑÑÑаниÑаÑ
модов в ÐаÑÑеÑÑкой ÑепеÑÑ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð½Ð¾, какие Ñеплики пÑÐ¸Ð½Ð°Ð´Ð»ÐµÐ¶Ð°Ñ ÐºÐ°Ð¶Ð´Ð¾Ð¼Ñ Ð¿ÐµÑÑонажÑ.","УлÑÑÑение: ÐовÑÑена ÑаÑÑоÑа Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾ÑобÑÐ°Ð¶ÐµÐ½Ð¸Ñ Ð·Ð²Ñковой волнÑ, поÑÑÐ¾Ð¼Ñ ÑÑÐ¾Ð²ÐµÐ½Ñ Ð·Ð²Ñка во вÑÐµÐ¼Ñ Ð·Ð°Ð¿Ð¸Ñи оÑобÑажаеÑÑÑ Ð¿Ð»Ð°Ð²Ð½ÐµÐµ.","ÐÑпÑавление оÑибки: ÐÑпÑавлена пÑоблема, из-за коÑоÑой игÑа в Ñежиме вÑбоÑа Ñолей не могла пÑодолжиÑÑÑÑ, еÑли в моде бÑло болÑÑе 24 пеÑÑонажей. ÐÐ°Ð½ÐµÐ»Ñ Ð²ÑбоÑа Ñолей Ñакже ÑлÑÑÑена, ÑÑÐ¾Ð±Ñ Ð±Ð¾Ð»ÑÑие ÑпиÑки пеÑÑонажей бÑло Ñдобнее пÑоÑмаÑÑиваÑÑ."]}},uk:{"Version v1.5.5":{label:"ÐеÑÑÑÑ v1.5.5",title:"Voxalike v1.5.5 â подаÑÑнки Supporter й Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð±Ð°Ð³Ð°ÑокоÑиÑÑÑваÑÑÐºÐ¾Ñ Ð³Ñи",changes:["ФÑнкÑÑÑ: Ð¢ÐµÐ¿ÐµÑ Supporter можна подаÑÑваÑи безпоÑеÑеднÑо ÑнÑÐ¾Ð¼Ñ Ð³ÑавÑевÑ, викоÑиÑÑовÑÑÑи адÑеÑÑ ÐµÐ»ÐµÐºÑÑÐ¾Ð½Ð½Ð¾Ñ Ð¿Ð¾ÑÑи, пÑивâÑÐ·Ð°Ð½Ñ Ð´Ð¾ його облÑкового запиÑÑ Voxalike. ÐлаÑÐ½Ñ Ð¿ÑивâÑÐ·Ð°Ð½Ñ Ð°Ð´ÑеÑÑ Ð¼Ð¾Ð¶Ð½Ð° пеÑеглÑнÑÑи в ÑоздÑÐ»Ñ Â«ÐÐ°Ð½Ñ Ð¾Ð±Ð»Ñкового запиÑÑ».","ФÑнкÑÑÑ: Також можна кÑпÑваÑи подаÑÑÐ½ÐºÐ¾Ð²Ñ ÐºÐ¾Ð´Ð¸ Supporter. ÐÑÑÐ»Ñ Ð¾Ð¿Ð»Ð°Ñи Ð²Ð¸Ð´Ð°Ð½Ñ ÐºÐ¾Ð´Ð¸ зâÑвлÑÑÑÑÑ Ð½Ð° ÑÑоÑÑнÑÑ Ð¿ÑдÑÑимки. Щоб акÑивÑваÑи код, вÑдкÑийÑе Ð¼ÐµÐ½Ñ Ð°Ð²Ð°ÑаÑа Ñ Ð²ÐµÑÑ
нÑÐ¾Ð¼Ñ Ð¿ÑÐ°Ð²Ð¾Ð¼Ñ ÐºÑÑÑ, вибеÑÑÑÑ Â«ÐкÑивÑваÑи код» Ñ Ð²Ð²ÐµÐ´ÑÑÑ Ð¹Ð¾Ð³Ð¾.","ФÑнкÑÑÑ: ÐÑд ÑÐ°Ñ Ð±Ð°Ð³Ð°ÑокоÑиÑÑÑваÑÑÐºÐ¾Ñ Ð³Ñи ÑÐµÐ¿ÐµÑ Ð´Ð¾ÑÑÑпний ÑекÑÑовий ÑаÑ. ÐнÑÑÑÑÑнÑоÑгÑовий ÑÐ°Ñ Ñакож показÑÑ Ð¿Ð¾ÑоÑÐ½Ñ ÐºÑлÑкÑÑÑÑ Ð³ÑавÑÑв Ñ ÐºÑмнаÑÑ Ñа повÑдомлÑÑ, коли гÑавеÑÑ Ð²Ð¸Ñ
одиÑÑ.","ФÑнкÑÑÑ: У пÑоÑÑÐ»Ñ Ð³ÑавÑÑ ÑÐµÐ¿ÐµÑ Ð¼Ð¾Ð¶Ðµ вÑдобÑажаÑиÑÑ ÑÑаÑиÑÑика багаÑокоÑиÑÑÑваÑÑÐºÐ¾Ñ Ð³Ñи, зокÑема кÑлÑкÑÑÑÑ Ð·Ð°Ð²ÐµÑÑениÑ
маÑÑÑв Co-op Ñ PvP Ñа ÑеÑÐµÐ´Ð½Ñ Ð¾ÑÑнки. У ÑоздÑÐ»Ñ Â«ÐÐ°Ð½Ñ Ð¾Ð±Ð»Ñкового запиÑÑ» ÑÑ ÑÑаÑиÑÑÐ¸ÐºÑ Ð¼Ð¾Ð¶Ð½Ð° пÑиÑ
оваÑи вÑд ÑнÑиÑ
гÑавÑÑв; Ð´Ð»Ñ Ð²Ð°Ñ Ð²Ð¾Ð½Ð° залиÑиÑÑÑÑ Ð²Ð¸Ð´Ð¸Ð¼Ð¾Ñ.","ФÑнкÑÑÑ: Ðа ÑÑоÑÑнкаÑ
модÑв Ñ ÐайÑÑеÑÐ½Ñ ÑÐµÐ¿ÐµÑ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð½Ð¾, ÑÐºÑ ÑеплÑки належаÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñ Ð¿ÐµÑÑонажÑ.","ÐокÑаÑеннÑ: ÐÑдвиÑено ÑаÑÑоÑÑ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²ÑдобÑÐ°Ð¶ÐµÐ½Ð½Ñ Ð·Ð²ÑÐºÐ¾Ð²Ð¾Ñ Ñ
вилÑ, ÑÐ¾Ð¼Ñ ÑÑÐ²ÐµÐ½Ñ Ð·Ð²ÑÐºÑ Ð¿Ñд ÑÐ°Ñ Ð·Ð°Ð¿Ð¸ÑÑ Ð²ÑдобÑажаÑÑÑÑÑ Ð¿Ð»Ð°Ð²Ð½ÑÑе.","ÐипÑÐ°Ð²Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ¸: ÐипÑавлено пÑоблемÑ, ÑеÑез ÑÐºÑ Ð³Ñа в ÑÐµÐ¶Ð¸Ð¼Ñ Ð²Ð¸Ð±Ð¾ÑÑ Ñолей не могла пÑодовжиÑиÑÑ, ÑкÑо мод мÑÑÑив понад 24 пеÑÑонажÑ. ÐÐ°Ð½ÐµÐ»Ñ Ð²Ð¸Ð±Ð¾ÑÑ Ñолей Ñакож покÑаÑено, Ñоб Ð²ÐµÐ»Ð¸ÐºÑ ÑпиÑки пеÑÑонажÑв бÑло зÑÑÑнÑÑе пеÑеглÑдаÑи."]}}};function tA(e,o){if(e==="en")return o;const r=eA[e][o.label];return r?{...o,...r}:o}
153class vc extends Error{failure;duration;constructor(o,r,i){super(r),this.name="JudgeSoundValidationError",this.failure=o,this.duration=i}}async function oA(e,o,r){try{const i=typeof document<"u"?await rA(e,r.timeoutMs):await aA(e);if(i===void 0)return;if(!Number.isFinite(i)||i<=0)throw new vc("decode",`Judge sound has an invalid duration: ${o}.`);if(i>r.maxSeconds)throw new vc("too-long",`Judge sound is ${i.toFixed(2)} seconds long: ${o}.`,i)}catch(i){throw i instanceof vc?i:new vc("decode",`Judge sound could not be decoded: ${o}.`)}}async function rA(e,o){const r=URL.createObjectURL(e),i=document.createElement("audio");i.preload="metadata";try{return await new Promise((c,l)=>{let p=!1;const f=y=>{p||(p=!0,globalThis.clearTimeout(b),i.onloadedmetadata=null,i.onerror=null,y())},b=globalThis.setTimeout(()=>f(()=>l(new Error("timeout"))),o);i.onloadedmetadata=()=>f(()=>c(i.duration)),i.onerror=()=>f(()=>l(new Error("decode failed"))),i.src=r,i.load()})}finally{i.removeAttribute("src"),i.load(),URL.revokeObjectURL(r)}}async function aA(e){const o=globalThis.AudioContext??globalThis.webkitAudioContext;if(!o)return;const r=new o;try{return(await hu(r,await e.arrayBuffer(),e.type)).duration}finally{await r.close().catch(()=>{})}}const Wt={maxZipBytes:30*1024*1024,maxExpandedBytes:60*1024*1024,maxFiles:20,maxFileBytes:12*1024*1024,maxSoundSeconds:4,mediaDecodeTimeoutMs:1e4},nA="voxalike-judge-packs",iA=1,qt="judge-packs",Za="current",TS="pack:",sA=/^[a-z0-9][a-z0-9._-]{0,63}$/,cA=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,Jh={png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",webp:"image/webp"},_h={mp3:"audio/mpeg",wav:"audio/wav",ogg:"audio/ogg",webm:"audio/webm"},lA=new Set(["json",...Object.keys(Jh),...Object.keys(_h)]),Ch=["low","mid","high","elite"],su=["reveal",...Ch],Yv=1254,dA=8,uA=250,pA=1024,mA={duration:{left:112,top:169,right:1190,bottom:929},timing:{left:377,top:43,right:950,bottom:1178},energy:{left:25,top:15,right:1196,bottom:1254},pitch:{left:21,top:9,right:1210,bottom:1180}};async function Kv(e,o={}){if(!e.name.toLowerCase().endsWith(".zip"))throw new Error("Judge packs must be imported as ZIP files.");if(!e.size)throw new Error("The judge pack ZIP is empty.");if(e.size>Wt.maxZipBytes)throw new Error(`Judge pack ZIP is too large. Maximum size is ${Om(Wt.maxZipBytes)} MB.`);const{default:r}=await Ko(async()=>{const{default:I}=await import("./jszip.min-CK9NPRCS.js").then(B=>B.j);return{default:I}},__vite__mapDeps([5,2,6])),i=await e.arrayBuffer(),c=await r.loadAsync(i,{checkCRC32:!0}),l=Object.values(c.files).filter(I=>!I.dir);if(!l.length)throw new Error("The judge pack ZIP does not contain any files.");if(l.length>Wt.maxFiles)throw new Error(`Judge pack ZIP contains too many files. Maximum count is ${Wt.maxFiles}.`);const p=new Map;let f=0;for(const I of l){const B=I.unsafeOriginalName,L=Yh(B??I.name),_=xc(L);if(!lA.has(_))throw new Error(`Judge pack contains a prohibited file type: ${L}.`);if(_==="json"&&L.toLowerCase()!=="judge_pack.json")throw new Error(`Only the root judge_pack.json manifest is allowed: ${L}.`);const oe=await I.async("uint8array");if(oe.byteLength>Wt.maxFileBytes)throw new Error(`${L} is too large. Maximum file size is ${Om(Wt.maxFileBytes)} MB.`);if(f+=oe.byteLength,f>Wt.maxExpandedBytes)throw new Error(`Judge pack expands beyond ${Om(Wt.maxExpandedBytes)} MB.`);if(p.has(L.toLowerCase()))throw new Error(`Judge pack contains a duplicate path: ${L}.`);p.set(L.toLowerCase(),oe)}const b=p.get("judge_pack.json");if(!b)throw new Error("judge_pack.json must be present at the root of the ZIP.");let y;try{y=JSON.parse(new TextDecoder("utf-8",{fatal:!0}).decode(b))}catch{throw new Error("judge_pack.json is not valid UTF-8 JSON.")}const w=yA(y),M={},E={},z={},N={},j={},D=[];for(const I of Et){const B=w.judges[I.key];if(!B)continue;if(B.name&&(z[I.key]=B.name),B.label&&(N[I.key]=B.label),B.feedback&&Object.keys(B.feedback).length&&(j[I.key]=B.feedback),B.image){const _=p.get(B.image.toLowerCase()),oe=xc(B.image),W=Jh[oe];if(!_)D.push(`${I.title} judge image is missing and will use the Voxalike default.`);else if(!W||!vA(_,oe))D.push(`${I.title}
153 judge image has an invalid file header and will use the Voxalike default.`);else{const O=_.slice().buffer,K=new Blob([O],{type:W});try{await MA(K,B.image);const T=await SA(K,I.key);M[I.key]=T.blob,T.warning&&D.push(`${I.title} ${T.warning}`)}catch{D.push(`${I.title} judge image could not be decoded and will use the Voxalike default.`)}}}const L={};for(const _ of su){const oe=B.sounds?.[_];if(!oe)continue;const W=p.get(oe.toLowerCase()),O=xc(oe),K=_h[O];if(!W)D.push(`${I.title} ${_} sound is missing and will be skipped.`);else if(!K||!kA(W,O))D.push(`${I.title} ${_} sound has an invalid file header and will be skipped.`);else{const T=W.slice().buffer,Y=new Blob([T],{type:K});try{await oA(Y,oe,{maxSeconds:Wt.maxSoundSeconds,timeoutMs:Wt.mediaDecodeTimeoutMs}),L[_]=Y}catch(X){if(X instanceof vc&&X.failure==="too-long"){const me=X.duration?.toFixed(1)??`more than ${Wt.maxSoundSeconds}`;D.push(`${I.title} ${_} sound is ${me} seconds long; the maximum is ${Wt.maxSoundSeconds} seconds, so it will be skipped.`)}else D.push(`${I.title} ${_} sound could not be decoded and will be skipped.`)}}}Object.keys(L).length&&(E[I.key]=L)}if(!Object.keys(M).length&&!Object.keys(E).length&&!Object.keys(z).length&&!Object.keys(N).length&&!Object.keys(j).length)throw new Error("Judge pack needs at least one valid judge image, sound, name, label, or feedback line.");return CA({id:w.id,title:w.title,author:w.author,version:w.version,packageHash:await EA(i),workshopId:o.workshopId,workshopSlug:o.workshopSlug,names:z,labels:N,feedback:j,images:M,sounds:E,warnings:D},o)}async function hA(){if(!Hi())return;const e=await Ji();try{const o=e.transaction(qt,"readonly"),r=await jc(o.objectStore(qt).get(Za));return await _i(o),$i(r)?ku(r):void 0}finally{e.close()}}async function Zv(){if(!Hi())return[];const e=await Ji();try{const o=e.transaction(qt,"readonly"),r=await jc(o.objectStore(qt).getAll());await _i(o);const i=r.find(l=>l.key===Za),c=new Map;for(const l of r)l.key.startsWith(TS)&&$i(l)&&c.set(l.id,l);return $i(i)&&!c.has(i.id)&&c.set(i.id,i),[...c.values()].sort((l,p)=>i?.id===l.id?-1:i?.id===p.id?1:p.savedAt-l.savedAt).map(ku)}finally{e.close()}}async function gA(e){if(!Hi())throw new Error("Browser storage is unavailable.");const o=await Ji();try{const r=o.transaction(qt,"readwrite"),i=r.objectStore(qt),c=await jc(i.get(Kh(e))),l=await jc(i.get(Za)),p=$i(c)?c:$i(l)&&l.id===e?l:void 0;if(!p)throw new Error("Judge mod is missing from local storage.");return i.put({...p,key:Za}),await _i(r),ku(p)}finally{o.close()}}async function fA(){if(!Hi())return;const e=await Ji();try{const o=e.transaction(qt,"readwrite");o.objectStore(qt).delete(Za),await _i(o)}finally{e.close()}}async function bA(e){if(!Hi())return;const o=await Ji();try{const r=o.transaction(qt,"readwrite"),i=r.objectStore(qt);i.delete(Kh(e));const c=await jc(i.get(Za));$i(c)&&c.id===e&&i.delete(Za),await _i(r)}finally{o.close()}}function Ba(e){e&&(Object.values(e.images).forEach(o=>{o&&!Object.values(Cc).includes(o)&&URL.revokeObjectURL(o)}),Object.values(e.sounds??{}).forEach(o=>{Object.values(o??{}).forEach(r=>{r&&URL.revokeObjectURL(r)})}))}function Yh(e){const o=e.trim();if(!o||o.includes("\0")||cA.test(o)||o.startsWith("/")||o.startsWith("\\")||/^[a-z]:/i.test(o)||o.includes("\\"))throw new Error(`Unsafe judge pack path: ${e}.`);const r=o.split("/");if(r.some(i=>!i||i==="."||i===".."))throw new Error(`Unsafe judge pack path: ${e}.`);return r.join("/")}function yA(e){if(!Ln(e))throw new Error("judge_pack.json must contain a JSON object.");if(e.schemaVersion!==1)throw new Error("Unsupported judge pack schemaVersion. Judge Pack MVP requires schemaVersion 1.");const o=Qv(e.id,"Judge pack id",64).toLowerCase();if(!sA.test(o))throw new Error("Judge pack id must use lowercase letters, numbers, dots, underscores, or hyphens.");const r=Qv(e.title,"Judge pack title",80),i=kc(e.author,80),c=kc(e.version,32),l=e.judges;if(!Ln(l))throw new Error("Judge pack judges must be a JSON object.");const p=new Set(Et.map(y=>y.key)),f=Object.keys(l).find(y=>!p.has(y));if(f)throw new Error(`Unknown judge slot: ${f}.`);const b={};for(const y of Et){const w=l[y.key];if(w==null)continue;const M=typeof w=="string"?{image:w}:Ln(w)?{image:w.image,name:w.name,label:w.label,feedback:jA(w.feedback,y.title),sounds:TA(w.sounds,y.title)}:void 0;if(!M)throw new Error(`${y.title} judge must be an image path or settings object.`);let E;if(M.image!==void 0&&M.image!==null&&M.image!==""){if(typeof M.image!="string")throw new Error(`${y.title} judge image must be a relative path.`);E=Yh(M.image);const I=xc(E);if(!Jh[I])throw new Error(`${y.title} judge image must use PNG, JPG, JPEG, or WebP.`)}const z=kc(M.name,24),N=kc(M.label,24),j=M.feedback,D=M.sounds;if(!E&&!z&&!N&&!j&&!D)throw new Error(`${y.title} judge must define an image, name, label, feedback, or sound.`);b[y.key]={image:E,name:z,label:N,feedback:j,sounds:D}}if(!Object.keys(b).length)throw new Error("Judge pack must declare at least one judge.");return{schemaVersion:1,id:o,title:r,author:i,version:c,judges:b}}function vA(e,o){return o==="png"?Mh(e,[137,80,78,71,13,10,26,10]):o==="jpg"||o==="jpeg"?Mh(e,[255,216,255]):o==="webp"?Oi(e,0,"RIFF")&&Oi(e,8,"WEBP"):!1}function kA(e,o){return o==="mp3"?Oi(e,0,"ID3")||e[0]===255&&(e[1]&224)===224:o==="wav"?Oi(e,0,"RIFF")&&Oi(e,8,"WAVE"):o==="ogg"?Oi(e,0,"OggS"):o==="webm"?Mh(e,[26,69,223,163]):!1}function wA(e,o,r,i){if(!Number.isFinite(e)||!Number.isFinite(o)||e<=0||o<=0)throw new Error("Judge image dimensions are invalid");const c=mA[i],l=Math.max(1,r.right-r.left),p=Math.max(1,r.bottom-r.top),f=c.bottom-c.top,b=Yv-32,y=Math.min(f/p,b/l),w=(c.left+c.right)/2,M=(r.left+r.right)/2;return{canvasSize:Yv,scale:y,drawX:w-M*y,drawY:c.bottom-r.bottom*y}}async function SA(e,o){if(typeof document>"u")return{blob:e};const r=await RA(e);try{const i=Math.min(1,pA/Math.max(r.width,r.height)),c=Math.max(1,Math.round(r.width*i)),l=Math.max(1,Math.round(r.height*i)),p=document.createElement("canvas");p.width=c,p.height=l;const f=p.getContext("2d",{willReadFrequently:!0});if(!f)return{blob:e,warning:"judge image could not be size-normalized and was kept as provided."};f.clearRect(0,0,c,l),f.drawImage(r.source,0,0,c,l);const b=f.getImageData(0,0,c,l).data,y=xA(b,c,l);if(!y.hasTransparency)return{blob:e,warning:"judge image has no transparent background, so its original sizing was kept."};if(!y.bounds)throw new Error("Judge image is fully transparent");const w=1/i,M={left:y.bounds.left*w,top:y.bounds.top*w,right:y.bounds.right*w,bottom:y.bounds.bottom*w},E=wA(r.width,r.height,M,o),z=document.createElement("canvas");z.width=E.canvasSize,z.height=E.canvasSize;const N=z.getContext("2d");return N?(N.clearRect(0,0,E.canvasSize,E.canvasSize),N.imageSmoothingEnabled=!0,N.imageSmoothingQuality="high",N.drawImage(r.source,E.drawX,E.drawY,r.width*E.scale,r.height*E.scale),{blob:await PA(z)}):{blob:e,warning:"judge image could not be size-normalized and was kept as provided."}}finally{r.release()}}function xA(e,o,r){let i=o,c=r,l=0,p=0,f=!1,b=!1;for(let y=0;y<r;y+=1)for(let w=0;w<o;w+=1){const M=e[(y*o+w)*4+3]??0;M<uA&&(b=!0),!(M<=dA)&&(f=!0,i=Math.min(i,w),c=Math.min(c,y),l=Math.max(l,w+1),p=Math.max(p,y+1))}return{bounds:f?{left:i,top:c,right:l,bottom:p}:void 0,hasTransparency:b}}
153async function RA(e){if(typeof createImageBitmap=="function"){const r=await createImageBitmap(e);return{source:r,width:r.width,height:r.height,release:()=>r.close()}}const o=URL.createObjectURL(e);try{const r=await new Promise((i,c)=>{const l=new Image,p=globalThis.setTimeout(()=>c(new Error("timeout")),Wt.mediaDecodeTimeoutMs);l.onload=()=>{globalThis.clearTimeout(p),i(l)},l.onerror=()=>{globalThis.clearTimeout(p),c(new Error("decode failed"))},l.src=o});return{source:r,width:r.naturalWidth,height:r.naturalHeight,release:()=>URL.revokeObjectURL(o)}}catch(r){throw URL.revokeObjectURL(o),r}}function PA(e){return new Promise((o,r)=>{e.toBlob(i=>{i?o(i):r(new Error("Judge image could not be encoded"))},"image/webp",.94)})}async function CA(e,o={}){const r=await Ji(),i={key:Za,id:e.id,title:e.title,author:e.author,version:e.version,packageHash:e.packageHash,workshopId:e.workshopId,workshopSlug:e.workshopSlug,savedAt:Date.now(),names:e.names,labels:e.labels,feedback:e.feedback,images:e.images,sounds:e.sounds,warnings:e.warnings};try{const c=r.transaction(qt,"readwrite"),l=c.objectStore(qt);return l.put({...i,key:Kh(i.id)}),o.enable!==!1&&l.put(i),await _i(c),ku(i)}finally{r.close()}}function ku(e){const o={},r={};for(const i of Et){const c=e.images[i.key];c&&(o[i.key]=URL.createObjectURL(c));const l=e.sounds?.[i.key];if(l){const p={};for(const f of su){const b=l[f];b&&(p[f]=URL.createObjectURL(b))}Object.keys(p).length&&(r[i.key]=p)}}return{id:e.id,title:e.title,author:e.author,version:e.version,packageHash:e.packageHash,workshopId:e.workshopId,workshopSlug:e.workshopSlug,savedAt:e.savedAt,names:e.names,labels:e.labels,feedback:e.feedback,images:o,sounds:r,warnings:e.warnings}}function Kh(e){return`${TS}${e}`}function $i(e){return!Ln(e)||typeof e.id!="string"||typeof e.title!="string"||typeof e.savedAt!="number"||!Ln(e.images)?!1:Object.entries(e.images).every(([o,r])=>Et.some(i=>i.key===o)&&r instanceof Blob&&r.size>0)}async function MA(e,o){if(typeof createImageBitmap=="function")try{const i=await createImageBitmap(e),c=i.width>0&&i.height>0;if(i.close(),!c)throw new Error("empty image");return}catch{throw new Error(`Judge image could not be decoded: ${o}.`)}if(typeof Image>"u")return;const r=URL.createObjectURL(e);try{await new Promise((i,c)=>{const l=new Image,p=globalThis.setTimeout(()=>c(new Error("timeout")),Wt.mediaDecodeTimeoutMs);l.onload=()=>{globalThis.clearTimeout(p),l.naturalWidth>0&&l.naturalHeight>0?i():c(new Error("empty image"))},l.onerror=()=>{globalThis.clearTimeout(p),c(new Error("decode failed"))},l.src=r})}catch{throw new Error(`Judge image could not be decoded: ${o}.`)}finally{URL.revokeObjectURL(r)}}function jA(e,o){if(e==null)return;if(!Ln(e))throw new Error(`${o} judge feedback must be a JSON object.`);const r=Object.keys(e).find(c=>!Ch.includes(c));if(r)throw new Error(`${o} judge feedback has an unknown tone: ${r}.`);const i={};for(const c of Ch){const l=kc(e[c],40);l&&(i[c]=l)}return Object.keys(i).length?i:void 0}function TA(e,o){if(e==null||e==="")return;if(typeof e=="string")return{reveal:Xv(e,`${o} reveal`)};if(!Ln(e))throw new Error(`${o} judge sounds must be a path or JSON object.`);const r=Object.keys(e).find(c=>!su.includes(c));if(r)throw new Error(`${o} judge sounds has an unknown key: ${r}.`);const i={};for(const c of su){const l=e[c];if(!(l==null||l==="")){if(typeof l!="string")throw new Error(`${o} ${c} sound must be a relative path.`);i[c]=Xv(l,`${o} ${c}`)}}return Object.keys(i).length?i:void 0}function Xv(e,o){const r=Yh(e),i=xc(r);if(!_h[i])throw new Error(`${o} sound must use MP3, WAV, OGG, or WebM.`);return r}function xc(e){const o=e.split("/").at(-1)??"",r=o.lastIndexOf(".");return r>=0?o.slice(r+1).toLowerCase():""}async function EA(e){if(!globalThis.crypto?.subtle)throw new Error("This browser cannot verify judge pack hashes.");const o=await globalThis.crypto.subtle.digest("SHA-256",e);return Array.from(new Uint8Array(o),r=>r.toString(16).padStart(2,"0")).join("")}function Mh(e,o){return o.every((r,i)=>e[i]===r)}function Oi(e,o,r){return[...r].every((i,c)=>e[o+c]===i.charCodeAt(0))}function Qv(e,o,r){if(typeof e!="string")throw new Error(`${o} is required.`);const i=ES(e,r);if(!i)throw new Error(`${o} is required.`);return i}function kc(e,o){return typeof e=="string"&&ES(e,o)||void 0}function ES(e,o){return e.replace(/\s+/g," ").trim().slice(0,o)}function Ln(e){return!!e&&typeof e=="object"&&!Array.isArray(e)}
153function Om(e){return Math.round(e/1024/1024)}function Hi(){return typeof indexedDB<"u"}function Ji(){return Hi()?new Promise((e,o)=>{const r=indexedDB.open(nA,iA);r.onupgradeneeded=()=>{const i=r.result;i.objectStoreNames.contains(qt)||i.createObjectStore(qt,{keyPath:"key"})},r.onsuccess=()=>e(r.result),r.onerror=()=>o(r.error??new Error("Browser storage failed.")),r.onblocked=()=>o(new Error("Browser storage is blocked by another tab."))}):Promise.reject(new Error("Browser storage is unavailable."))}function jc(e){return new Promise((o,r)=>{e.onsuccess=()=>o(e.result),e.onerror=()=>r(e.error??new Error("Browser storage failed."))})}function _i(e){return new Promise((o,r)=>{e.oncomplete=()=>o(),e.onerror=()=>r(e.error??new Error("Browser storage failed.")),e.onabort=()=>r(e.error??new Error("Browser storage was aborted."))})}const Um=[[0,0],[40,35],[60,55],[63,59],[74,74],[83,90],[88,95],[95,98],[99,99],[100,100]];function AS(e){const o=Math.max(0,Math.min(100,Number.isFinite(e)?e:0));for(let r=1;r<Um.length;r+=1){const[i,c]=Um[r];if(o>i)continue;const[l,p]=Um[r-1],f=(o-l)/Math.max(1,i-l);return Math.round(p+(c-p)*f)}return 100}const AA=1.02,IA=.5;function ek(e){return Math.max(0,Math.min(1,e))}function NA(e,o){const r=e.mode==="pvp"&&e.pvpFormat==="parallel",i=Date.parse(e.startedAt),c=o.map(y=>{const w=e.takes.filter(D=>D.playerId===y.id),M=w.map(D=>D.scoreTotal??D.score?.total??0),E=AS(M.length?M.reduce((D,I)=>D+I,0)/M.length:0),z=e.assignments.filter(D=>D.playerId===y.id).length,N=w.reduce((D,I)=>Math.max(D,Date.parse(I.createdAt)||0),0),j=r&&z>0&&w.length>=z&&Number.isFinite(i)&&N>=i?N-i:void 0;return{playerId:y.id,playerName:y.name,qualityScore:E,durationMs:j,takeCount:w.length,bestScore:M.length?Math.max(...M):0}}),l=c.map(y=>y.durationMs).filter(y=>y!==void 0),p=l.length?Math.min(...l):0,f=l.length?Math.max(...l):0,b=p*AA;return c.map(y=>{let w=0;r&&y.durationMs!==void 0&&(w=y.durationMs<=b||f<=b?1:ek(1-(y.durationMs-b)/(f-b)));const M=r?ek((y.qualityScore-70)/20):0,E=r?y.qualityScore+(100-y.qualityScore)*IA*M*w:y.qualityScore,z=y.qualityScore>0?E/y.qualityScore:1;return{...y,speedScore:w,qualityGate:M,multiplier:z,battleScore:E}}).sort((y,w)=>w.battleScore-y.battleScore||w.qualityScore-y.qualityScore||w.bestScore-y.bestScore||w.takeCount-y.takeCount)}function tk(e){if(e===void 0)return"â";const o=Math.max(0,Math.round(e/1e3)),r=Math.floor(o/60),i=o%60;return`${r}:${String(i).padStart(2,"0")}`}function DA(e){return Math.max(0,Math.min(1,Number.isFinite(e)?e:0))}function LA(e,o,r){const i=Math.max(.001,e),c=Math.max(0,o),l=Math.max(0,r),p=c+i+l;return{totalSeconds:p,contentStartProgress:c/p,contentEndProgress:(c+i)/p}}function jh(e,o){return DA(Math.max(0,e)/Math.max(.001,o))}function Sd(e,o,r){const i=Math.max(0,r);return jh(i+Math.max(0,e),i+Math.max(.001,o))}const VA=3e4,zA=15e3,OA=2e4,UA=55e3,FA='{"type":"ping"}';function BA(e){const o=Math.max(1,Math.floor(e));return Math.min(VA,1200*2**Math.min(o-1,5))}function WA(e,o,r=!1){const i=Math.max(0,Math.floor(e));return o==="playing"&&!r?i<2?5e3:i<5?1e4:15e3:i<1?5e3:i<3?15e3:3e4}function ok(e,o=Date.now()){const r=Date.parse(e);return Number.isFinite(r)&&r>o}function $A(e,o=!0){const r=e.toLowerCase();return!o||r.includes("failed to fetch")||r.includes("network")||r.includes("internet")||r.includes("load failed")||r.includes("abort")||r.includes("timeout")||r.includes("d1_error")||r.includes("overloaded")||r.includes("queued for too long")||r.includes("temporarily unavailable")||r.includes("service unavailable")||r.includes("internal server error")||r.includes("bad gateway")||r.includes("gateway timeout")}function GA(e,o,r){return!!(o&&e===o&&r)}function qA(e,o=Date.now()){return!Number.isFinite(e)||!Number.isFinite(o)?!0:o-e>=UA}function rk(e,o){const r=e?.trim(),i=o?.trim();return!!(r&&i&&r!=="unknown"&&i!=="unknown"&&r!==i)}function HA(e,o){return e.mode!=="coop"||o<0||o>=e.assignments.length||o>=e.currentAssignmentIndex?!1:!e.takes.some(r=>r.assignmentIndex===o)}function JA(){const e=m.useMemo(()=>new Jj,[]),[o,r]=m.useState(()=>e.getSnapshot()),i=m.useCallback(()=>e.validateConnection(),[e]),c=m.useCallback(l=>e.invalidateConnection(l),[e]);return m.useEffect(()=>{const l=()=>r(e.getSnapshot());return e.addEventListener("change",l),e.refreshDevices(),()=>{e.removeEventListener("change",l),e.destroy()}},[e]),{snapshot:o,requestPermission:(l,p)=>e.requestPermission(l,p),selectDevice:(l,p)=>e.selectDevice(l,p),validateConnection:i,invalidateConnection:c,stop:()=>e.stop()}}const _A="project-echo-local-packs",YA=1,st="pack-sources",Tc="current",cu="pack:";async function KA(e,o,r,i,c={}){const l=await Yi(),p=oI(e,o,r),f=r.map(aI),b={id:p,kind:e,name:o,savedAt:Date.now(),summary:i?{...i,title:Wi(i.title,o)}:void 0,files:f};try{const y=l.transaction(st,"readwrite");return Zh(y.objectStore(st),b),c.makeCurrent!==!1&&Xh(y.objectStore(st),b),await Vn(y),b}finally{l.close()}}async function ak(e){const o=await Yi();try{const r=o.transaction(st,"readwrite");Zh(r.objectStore(st),e),Xh(r.objectStore(st),e),await Vn(r)}finally{o.close()}}async function ZA(e){const o=await Yi();try{const r=o.transaction(st,"readwrite"),i=r.objectStore(st);Zh(i,e);const c=await Ac(i.get(Tc));Ec(c)?.id===e.id&&Xh(i,e),await Vn(r)}finally{o.close()}}async function XA(){if(!eg())return;const e=await Yi();try{const o=e.transaction(st,"readonly"),r=await Ac(o.objectStore(st).get(Tc));return await Vn(o),r?Ec(r):void 0}finally{e.close()}}async function nk(){if(!eg())return[];const e=await Yi();try{const o=e.transaction(st,"readonly"),r=await Ac(o.objectStore(st).getAll());await Vn(o);const i=r.find(l=>l.key===Tc)?Ec(r.find(l=>l.key===Tc)):void 0,c=new Map;for(const l of r){if(!l.key.startsWith(cu))continue;const p=Ec(l);p&&c.set(p.id,p)}return i&&!c.has(i.id)&&c.set(i.id,i),[...c.values()].sort((l,p)=>i?.id===l.id?-1:i?.id===p.id?1:p.savedAt-l.savedAt)}finally{e.close()}}async function QA(e){const o=new Set(e);if(!o.size)return;const r=await Yi();try{const i=r.transaction(st,"readwrite"),c=i.objectStore(st),l=await Ac(c.getAll());for(const b of l)ik(b,o)&&c.delete(b.key);await Vn(i);
153const p=r.transaction(st,"readonly"),f=await Ac(p.objectStore(st).getAll());if(await Vn(p),f.some(b=>ik(b,o)))throw new Error("Browser storage kept the deleted dub mod.")}finally{r.close()}}function ik(e,o){if(e.key.startsWith(cu)&&o.has(e.key.slice(cu.length)))return!0;const r=Ec(e)?.id;return!!(r&&o.has(r))}function Fm(e){return e.files.map(o=>{const r=new File([o.blob],o.name||nI(o.path),{lastModified:o.lastModified,type:o.type||o.blob.type});return o.path&&Object.defineProperty(r,"webkitRelativePath",{configurable:!0,value:o.path}),r})}function eI(e,o){return e==="zip"?o[0]?.name.replace(/\.[^.]+$/,"")||"local-zip":Qh(o[0]).split(/[\\/]/).filter(Boolean)[0]||"local-folder"}function Zh(e,o){e.put(IS(tI(o.id),o))}function Xh(e,o){e.put(IS(Tc,o))}function IS(e,o){return{key:e,id:o.id,kind:o.kind,name:o.name,savedAt:o.savedAt,summary:o.summary,files:o.files}}function Ec(e){if(e?.files?.length)return{id:e.id||NS(e.kind,e.name,e.files),kind:e.kind,name:e.name,savedAt:e.savedAt,summary:e.summary?{...e.summary,title:Wi(e.summary.title,e.name)}:void 0,files:e.files}}function tI(e){return`${cu}${e}`}function oI(e,o,r){const i=r.map(rI);return NS(e,o,i)}function NS(e,o,r){const i=r.map(f=>f.path).sort(),c=r.reduce((f,b)=>f+b.blob.size,0),l=r.reduce((f,b)=>Math.max(f,b.lastModified),0),p=[e,o,r.length,c,l,i[0]??"",i.at(-1)??""].join("|");return`${iI(o)||e}-${sI(p)}`}function rI(e){return{path:Qh(e),lastModified:e.lastModified,blob:e}}function aI(e){return{path:Qh(e),name:e.name,type:e.type,lastModified:e.lastModified,blob:e.slice(0,e.size,e.type)}}function Qh(e){return e?e.webkitRelativePath||e.name:""}function nI(e){return e.split(/[\\/]/).filter(Boolean).at(-1)||"file"}function iI(e){return e.toLowerCase().replace(/\.[^.]+$/,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}function sI(e){let o=2166136261;for(let r=0;r<e.length;r+=1)o^=e.charCodeAt(r),o=Math.imul(o,16777619);return(o>>>0).toString(36)}function eg(){return typeof indexedDB<"u"}function Yi(){return eg()?new Promise((e,o)=>{const r=indexedDB.open(_A,YA);r.onupgradeneeded=()=>{const i=r.result;i.objectStoreNames.contains(st)||i.createObjectStore(st,{keyPath:"key"})},r.onsuccess=()=>e(r.result),r.onerror=()=>o(r.error??new Error("Browser storage failed.")),r.onblocked=()=>o(new Error("Browser storage is blocked by another tab."))}):Promise.reject(new Error("Browser storage is unavailable."))}function Ac(e){return new Promise((o,r)=>{e.onsuccess=()=>o(e.result),e.onerror=()=>r(e.error??new Error("Browser storage failed."))})}function Vn(e){return new Promise((o,r)=>{e.oncomplete=()=>o(),e.onerror=()=>r(e.error??new Error("Browser storage failed.")),e.onabort=()=>r(e.error??new Error("Browser storage was aborted."))})}function lu(e){return[e.packId,e.workshopPackId??"",e.workshopPackSlug??"",Wi(e.packTitle),String(e.clipCount??"")].join(":")}
153function cI(e,o){return e.assignmentMode!=="role"||!o?e:{...e,packId:o.packId,packTitle:o.packTitle,packSource:o.packSource,workshopPackId:o.workshopPackId,workshopPackSlug:o.workshopPackSlug,clipCount:o.clipCount??e.clipCount}}function lI(e,o,r,i,c="Wait for disconnected players to reconnect"){if(!e)return"Create or join a room first";if(e.players.length<2)return"At least 2 players are required";const l=e.recovery?"coop":o;if(l==="pvp"&&e.players.length>8)return"PVP supports up to 8 players. Switch to Co-op mode or ask players to leave.";if(e.players.some(y=>y.reconnectDeadlineAt))return c;const p=e.players.find(y=>!y.isHost&&!y.isReady);if(p)return`Waiting for ${p.name||"Player"} to ready up`;if(e.recovery)return!e.lobbyPack||!i?"Install the exact dub mod used by this Recovery ZIP before starting":void 0;if(!r)return"Choose a dub mod before starting";if(!e.lobbyPack||lu(e.lobbyPack)!==lu(r))return"Syncing room mod";const f=Math.max(0,Math.round(r.clipCount??0));if((l==="pvp"?f*e.players.length:f)>500)return l==="pvp"?`This mod has too many clips for PVP with ${e.players.length} players. Switch to a smaller mod, fewer players, or Co-op mode.`:"This mod has too many clips for a room. Switch to a smaller mod."}function dI(e,o){const r={roomCode:e.roomCode,playerToken:e.playerToken};return e.recovery?r:{...r,game:o()}}function uI(e){const o=e.speakers.map(r=>r.trim()).find(Boolean);if(o)return o;for(const[r,i]of Object.entries(e.metadata)){if(!r.toLowerCase().includes("character"))continue;const c=i.replace(/[[\]"']/g,"").split(/[,/|]/).map(l=>l.trim()).find(Boolean);if(c)return c}}function pI(e,o,r){if(!o.length||!r.length)throw new Error("Claim every role before starting");const i=new Map(r.map(c=>[c.role.toLocaleLowerCase(),c]));return e.map((c,l)=>{const p=uI(c),f=p?i.get(p.toLocaleLowerCase()):void 0;if(p&&!f)throw new Error(`No player has claimed ${p}.`);const b=o[l%o.length];return{index:l,clipId:c.id,title:c.title,caption:c.caption,role:p,playerId:f?.playerId??b.id,playerName:f?.playerName??b.name}})}function Fd(e,o){return o.summary?.workshopId===e.id||!!(o.summary?.workshopSlug&&At(o.summary.workshopSlug)===At(e.slug))}function Th(e,o){if(Fd(e,o))return!0;if(o.summary?.workshopId||o.summary?.workshopSlug)return!1;const r=At(e.title),i=At(e.slug);if(![o.summary?.title,o.name].map(At).filter(Boolean).some(f=>f===r||f===i))return!1;const l=At(e.creator),p=At(o.summary?.creator);return!(l&&p&&l!==p)}function sk(e,o){return o.filter(r=>Th(e,r))}function DS(e,o){const r=!!(e.workshopPackId&&o.workshopPackId&&e.workshopPackId===o.workshopPackId),i=!!(e.workshopPackSlug&&o.workshopPackSlug&&At(e.workshopPackSlug)===At(o.workshopPackSlug));return r||i?!0:e.workshopPackId||e.workshopPackSlug||o.workshopPackId||o.workshopPackSlug?!1:!!(e.packId&&o.packId&&e.packId===o.packId)}function Bm(e,o){const r=e.summary?.workshopId,i=e.summary?.workshopSlug,c=!!(o.workshopPackId||o.workshopPackSlug),l=!!(r||i);return!!(o.workshopPackId&&r===o.workshopPackId)||!!(o.workshopPackSlug&&i&&At(i)===At(o.workshopPackSlug))?xd(e,o):c&&l?!1:c?ck(e,o)&&xd(e,o):(e.id===o.packId||ck(e,o))&&xd(e,o)}function ck(e,o){const r=At(o.packTitle);return r?[e.summary?.title,e.name].map(At).filter(Boolean).some(c=>c===r):!0}function xd(e,o){const r=o.clipCount,i=e.summary?.clipCount;if(r&&i&&r!==i)return!1;const c=At(o.creator),l=At(e.summary?.creator);if(c&&l&&c!==l)return!1;const p=o.videoDurationSeconds,f=e.summary?.videoDurationSeconds;return!(p&&f&&Math.abs(p-f)>1.5)}function At(e){return(e??"").trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")}const tg="voxalike:active-run-diagnostics:v1",Rc=vI(),mI=fI();function hI(){return mI}function gI(e){const o=rg(),r=new Date().toISOString(),i={schemaVersion:1,bootId:Rc,startedAt:o?.bootId===Rc?o.startedAt:r,updatedAt:r,path:`${window.location.pathname}${window.location.search}`,online:navigator.onLine,secureContext:window.isSecureContext,browser:navigator.userAgent,...o?.bootId===Rc&&o.lastIssue?{lastIssue:o.lastIssue}:{},...e};LS(i)}function Vi(){try{window.sessionStorage.removeItem(tg)}catch{}}function og(e,o,r){const i=rg();if(!i||i.bootId!==Rc)return;const c=bI(o);i.lastIssue={kind:e,name:c.name,message:c.message,stack:[c.stack,r].filter(
153Boolean).join(` 154`)||void 0,occurredAt:new Date().toISOString()},i.updatedAt=new Date().toISOString(),LS(i)}function fI(){const e=rg();if(!e||e.bootId===Rc)return;Vi();const o=e.lastIssue,r=e.clipIndex===void 0?"unknown":String(e.clipIndex+1);return{message:"Voxalike reloaded while a game was active. Copy this report if the interruption happens again.",technical:["Voxalike unexpected run exit diagnostics",`Detected: ${new Date().toISOString()}`,`Last update: ${e.updatedAt}`,`Mode: ${e.mode}`,`Phase: ${e.phase}`,`Mod: ${e.modTitle??"unknown"}`,`Mod ID: ${e.modId??"unknown"}`,`Clip: ${r}${e.clipCount===void 0?"":` / ${e.clipCount}`} · ${e.clipTitle??"unknown"}`,`Clip ID: ${e.clipId??"unknown"}`,`Runtime issue: ${o?`${o.kind} · ${o.name}: ${o.message}`:"none captured (page or renderer stopped before JavaScript could report an error)"}`,...o?.occurredAt?[`Issue time: ${o.occurredAt}`]:[],...o?.stack?["Stack:",kI(o.stack,6e3)]:[],`Page: ${e.path}`,`Online: ${e.online?"yes":"no"}`,`Secure context: ${e.secureContext?"yes":"no"}`,`Browser: ${e.browser}`].join(` 155`)}}function rg(){try{const e=window.sessionStorage.getItem(tg);if(!e)return;const o=JSON.parse(e);return o.schemaVersion!==1||typeof o.bootId!="string"||typeof o.mode!="string"?void 0:o}catch{return}}function LS(e){try{window.sessionStorage.setItem(tg,JSON.stringify(e))}catch{}}function bI(e){return e instanceof Error?{name:e.name||"Error",message:e.message||"No error message was returned.",stack:e.stack}:{name:"UnknownError",message:typeof e=="string"?e:yI(e)}}function yI(e){try{return JSON.stringify(e)||String(e??"No error message was returned.")}catch{return String(e??"No error message was returned.")}}function vI(){try{return globalThis.crypto?.randomUUID?.()??`${Date.now()}-${Math.random().toString(36).slice(2)}`}catch{return`${Date.now()}-${Math.random().toString(36).slice(2)}`}}function kI(e,o){return e.length<=o?e:`${e.slice(0,o)} 156[stack truncated]`}const wI=10080*60*1e3,SI=5;function VS(e,o=Date.now()){return e.filter(r=>{const i=Date.parse(r.completedAt);return Number.isFinite(i)&&i+wI>o}).sort((r,i)=>Date.parse(i.completedAt)-Date.parse(r.completedAt)).filter((r,i,c)=>c.findIndex(l=>l.id===r.id)===i).slice(0,SI)}function xI(e,o){return e.find(r=>{if(r.mode!=="multi"||o.viewerPlayerId&&r.playerId&&o.viewerPlayerId!==r.playerId)return!1;const i=r.roomGame;return i?.startedAt===o.game.startedAt||!!r.roomRunKey?.endsWith(`:${o.game.startedAt}`)?i?DS(i,o.game):r.packId===o.game.packId:!1})}function RI(e,o){return e.game.takes.filter(r=>e.game.mode==="coop"||!o||r.playerId===o).map(r=>`room:${r.assignmentIndex}:${r.clipId}`)}const PI=1,CI=3,MI=12e3,Rd=8e3,jI=8e3;class ag extends Error{constructor(o){super(o===2?"Source video failed because of a network error.":o===3?"Source video decoding failed.":o===4?"Source video format is not supported by this browser.":"Source video failed to load or decode."),this.mediaErrorCode=o,this.name="ExportVideoMediaError"}mediaErrorCode}class Bi extends Error{constructor(o,r){super(`Source video could not start after ${o} attempts. ${r instanceof Error?r.message:"Unknown media error."}`),this.name="ExportVideoStartupError",this.mediaErrorCode=r instanceof ag?r.mediaErrorCode:void 0}mediaErrorCode}function Eh(e){return e instanceof Error&&(e.name==="NotAllowedError"||/not allowed by the user agent|user gesture|playback is not allowed/i.test(e.message))}async function TI(e,o={}){const r=Math.max(1,o.attempts??CI);let i;for(let c=1;c<=r;c+=1){ig(o.signal),o.onAttempt?.(c,r);try{e.pause(),e.currentTime=0,e.load(),await EI(e,PI,"loadedmetadata",o.metadataTimeoutMs??MI,o.signal),await dk(e,o.playbackTimeoutMs??Rd,o.signal);const l=e.currentTime;try{await at(e.play(),o.playbackTimeoutMs??Rd,"Source video playback did not start.",o.signal)}catch(p){if(!Eh(p)||!o.onPlaybackBlocked)throw p;await o.onPlaybackBlocked(e,o.signal)}return await AI(e,l,o.playbackTimeoutMs??Rd,o.signal),e.pause(),e.currentTime=0,await dk(e,o.playbackTimeoutMs??Rd,o.signal),c}catch(l){if(NI(l))throw l;if(Eh(l)||l instanceof ag&&l.mediaErrorCode===4)throw new Bi(c,l);i=l,e.pause(),c<r&&await II(350*c,o.signal)}}throw new Bi(r,i)}async function at(e,o,r,i){return ig(i),new Promise((c,l)=>{const p=setTimeout(()=>b(()=>l(new Error(r))),Math.max(1,o)),f=()=>b(()=>l(wu())),b=y=>{clearTimeout(p),i?.removeEventListener("abort",f),y()};i?.addEventListener("abort",f,{once:!0}),e.then(y=>b(()=>c(y)),y=>b(()=>l(y)))})}function lk(e,o,r=jI){return e.seeking?(ig(o),new Promise((i,c)=>{const l=setTimeout(()=>y(()=>c(new Error("Source video timed out while seeking during export."))),Math.max(1,r)),p=()=>y(i),f=()=>y(()=>c(new Error("Source video failed while seeking during export."))),b=()=>y(()=>c(wu())),y=w=>{clearTimeout(l),e.removeEventListener("seeked",p),e.removeEventListener("error",f),o?.removeEventListener("abort",b),w()};e.addEventListener("seeked",p,{once:!0}),e.addEventListener("error",f,{once:!0}),o?.addEventListener("abort",b,{once:!0})})):Promise.resolve()}function EI(e,o,r,i,c){return e.readyState>=o?Promise.resolve():ng(e,r,i,c,()=>e.readyState>=o)}async function dk(e,o,r){e.seeking&&await ng(e,"seeked",o,r,()=>!e.seeking)}function AI(e,o,r,i){return ng(e,"timeupdate",r,i,()=>e.currentTime>o+.02)}function ng(e,o,r,i,c){return c()?Promise.resolve():new Promise((l,p)=>{const f=setTimeout(()=>M(()=>p(new Error(`Source video timed out while waiting for ${o}.`))),r),b=()=>{c()&&M(l)},y=()=>M(()=>p(new ag(e.error?.code??0))),w=()=>M(()=>p(wu())),M=E=>{clearTimeout(f),e.removeEventListener(o,b),e.removeEventListener("error",y),i?.removeEventListener("abort",w),E()};e.addEventListener(o,b),e.addEventListener("error",y,{once:!0}),i?.addEventListener("abort",w,{once:!0})})}function II(e,o){return at(new Promise(r=>setTimeout(r,e)),e+50,"Retry delay timed out.",o)}function wu(){const e=new Error("Video export cancelled.");return e.name="AbortError",e}function NI(e){return e instanceof Error&&e.name==="AbortError"}function ig(e){if(e?.aborted)throw wu()}
156async function DI(e){const r=new Uint8Array(await e.slice(0,262144).arrayBuffer()),i=e.size>262144?new Uint8Array(await e.slice(Math.max(262144,e.size-262144)).arrayBuffer()):new Uint8Array;return{av1:Pd(r,"av1C")||Pd(i,"av1C"),opus:Pd(r,"dOps")||Pd(i,"dOps")}}function Pd(e,o){const r=Array.from(o,i=>i.charCodeAt(0));for(let i=0;i<=e.length-r.length;i+=1)if(r.every((c,l)=>e[i+l]===c))return!0;return!1}const LI=3e3,Wm=6e3,VI=1.5,zI=.2;class Ah extends Error{constructor(o="Source video frames stopped advancing during export."){super(o),this.name="ExportVideoFrameStallError"}}function OI(e,o,r){return{usesPresentedFrames:r,startedAt:e,lastPlaybackTime:Math.max(0,o),lastPlaybackAt:e,lastPresentedMediaTime:Math.max(0,o),lastPresentedFrameAt:e,hasPresentedProgress:!1,lastDecodedFrameCount:0,lastDecodedFrameAt:e,hasDecodedFrameSignal:!1}}function UI(e,o,r){!Number.isFinite(o)||o<=e.lastPresentedMediaTime+.005||(e.lastPresentedMediaTime=o,e.lastPresentedFrameAt=r,e.hasPresentedProgress=!0)}function FI(e,o,r){Number.isFinite(o)&&o>e.lastPlaybackTime+.02&&(e.lastPlaybackTime=o,e.lastPlaybackAt=r)}function BI(e,o,r){if(!(!Number.isFinite(o)||o<0)){if(!e.hasDecodedFrameSignal){e.lastDecodedFrameCount=o,e.lastDecodedFrameAt=r,e.hasDecodedFrameSignal=!0;return}o>e.lastDecodedFrameCount&&(e.lastDecodedFrameCount=o,e.lastDecodedFrameAt=r)}}function WI(e,o,r,i){if(!Number.isFinite(o)||!Number.isFinite(r)||r<=0||i-e.startedAt<LI||o>=r-zI||!(o-e.lastPlaybackTime>=VI&&i-e.lastPlaybackAt>=Wm))return!1;const p=!e.hasDecodedFrameSignal||i-e.lastDecodedFrameAt>=Wm,f=!e.usesPresentedFrames||!e.hasPresentedProgress||i-e.lastPresentedFrameAt>=Wm;return p&&f}function $I(e){return e.mobile||typeof e.hardwareConcurrency=="number"&&e.hardwareConcurrency>0&&e.hardwareConcurrency<=4||typeof e.deviceMemoryGb=="number"&&e.deviceMemoryGb>0&&e.deviceMemoryGb<=4?"constrained":"standard"}function GI(e){return e==="constrained"?24:30}function qI(){return 15}function HI(e,o){return[e?{mimeType:e,audioBitsPerSecond:o}:{audioBitsPerSecond:o},e?{mimeType:e}:{}]}function JI(e){return e?["video/mp4","video/mp4;codecs=avc1.42E01E,mp4a.40.2","video/webm;codecs=vp8,opus","video/webm"]:["video/webm;codecs=vp9,opus","video/webm;codecs=vp8,opus","video/webm","video/mp4;codecs=avc1.42E01E,mp4a.40.2","video/mp4"]}function _I(e){return e.format==="dub"&&!e.subtitles&&e.supportsCaptureStream&&e.sourceWidth===e.outputWidth&&e.sourceHeight===e.outputHeight&&Number.isFinite(e.sourceDurationSeconds)&&e.sourceDurationSeconds>0&&e.timelineDurationSeconds<=e.sourceDurationSeconds+.2}const Cd={segment:408125543,info:357149030,timestampScale:2807729,duration:17545},YI=1e6,KI=1e9;async function ZI(e,o){if(!XI(e)||!Number.isFinite(o)||o<=0)return e;try{const r=new Uint8Array(await e.arrayBuffer()),i=QI(r,o);if(!i)return e;const c=new ArrayBuffer(i.byteLength);return new Uint8Array(c).set(i),new Blob([c],{type:e.type||"video/webm"})}catch{return e}}function XI(e){const o=e.type.toLowerCase();return o.includes("webm")||o.includes("matroska")}function QI(e,o){const r=Md(e,0,e.length,Cd.segment);if(!r)return;const i=Md(e,r.dataStart,r.dataEnd,Cd.info);if(!i||i.sizeUnknown)return;const c=Md(e,i.dataStart,i.dataEnd,Cd.timestampScale),l=c?iN(e,c):YI;if(!Number.isFinite(l)||l<=0)return;const p=o*KI/l,f=Md(e,i.dataStart,i.dataEnd,Cd.duration);return f?eN(e,f,p):tN(e,r,i,p)}function eN(e,o,r){const i=o.dataEnd-o.dataStart;if(i!==4&&i!==8)return;const c=new Uint8Array(e),l=new DataView(c.buffer,c.byteOffset,c.byteLength);return i===4?l.setFloat32(o.dataStart,r,!1):l.setFloat64(o.dataStart,r,!1),c}function tN(e,o,r,i){const c=oN(i),l=r.dataSize+c.length;if(!uk(l,r.sizeLength))return;const p=o.dataSize+c.length;if(!o.sizeUnknown&&!uk(p,o.sizeLength))return;const f=new Uint8Array(e.length+c.length);return f.set(e.subarray(0,r.dataEnd),0),f.set(c,r.dataEnd),f.set(e.subarray(r.dataEnd),r.dataEnd+c.length),f.set(pk(l,r.sizeLength),r.sizeOffset),o.sizeUnknown||f.set(pk(p,o.sizeLength),o.sizeOffset),f}function oN(e){const o=new Uint8Array(11);return o[0]=68,o[1]=137,o[2]=136,new DataView(o.buffer).setFloat64(3,e,!1),o}function Md(e,o,r,i){let c=o;for(;c<r;){const l=rN(e,c,r);if(!l)return;if(l.id===i)return l;if(l.sizeUnknown)return;c=l.dataEnd}}function rN(e,o,r){const i=aN(e,o,r);if(!i)return;const c=o+i.length,l=nN(e,c,r);if(!l)return;const p=c+l.length,f=l.unknown?r:p+l.value;if(!(f>r))return{id:i.value,start:o,idLength:i.length,sizeOffset:c,sizeLength:l.length,dataStart:p,dataEnd:f,dataSize:l.unknown?r-p:l.value,sizeUnknown:l.unknown}}function aN(e,o,r){if(o>=r)return;const i=zS(e[o],4);if(!i||o+i>r)return;let c=0;for(let l=0;l<i;l+=1)c=c*256+e[o+l];return{value:c,length:i}}function nN(e,o,r){if(o>=r)return;const i=zS(e[o],8);if(!i||o+i>r)return;const c=(1<<8-i)-1;let l=e[o]&c,p=c;for(let f=1;f<i;f+=1)l=l*256+e[o+f],p=p*256+255;return{value:l,length:i,unknown:l===p}}function zS(e,o){for(let r=1;r<=o;r+=1)if(e&1<<8-r)return r}function iN(e,o){let r=0;for(let i=o.dataStart;i<o.dataEnd;i+=1)r=r*256+e[i];return r}function uk(e,o){return Number.isInteger(e)&&e>=0&&e<sN(o)}
156function pk(e,o){const r=new Uint8Array(o);let i=e;for(let c=o-1;c>=0;c-=1)r[c]=i&255,i=Math.floor(i/256);return r[0]|=1<<8-o,r}function sN(e){let o=(1<<8-e)-1;for(let r=1;r<e;r+=1)o=o*256+255;return o}const mk=2*1024*1024,cN=1e9;async function hk(e,o){if(!hN(e)||!Number.isFinite(o)||o<=0)return e;try{const r=await lN(e,o);if(r)return r}catch(r){console.warn("Could not add a WebM seek index; falling back to duration metadata.",r)}return ZI(e,o)}async function lN(e,o){const r=await Ko(()=>import("./EBML.min-BfozmedC.js").then(w=>w.E),__vite__mapDeps([7,2,6])),i=mN(r);if(!i)return;const c=new i.Decoder,l=new i.Reader,p=dN();l.logging=!1;for(let w=0;w<e.size;w+=mk){const M=await e.slice(w,Math.min(e.size,w+mk)).arrayBuffer();c.decode(M).forEach(E=>{p.read(E),l.read(E)})}l.stop();const f=p.cues.length>0?p.cues:l.cues;if(l.metadatas.length===0||f.length===0||l.metadataSize<=0||l.metadataSize>=e.size||!Number.isFinite(l.timestampScale)||l.timestampScale<=0)return;const b=o*cN/l.timestampScale,y=i.tools.makeMetadataSeekable(l.metadatas,b,f);if(y.byteLength!==0)return new Blob([y,e.slice(l.metadataSize)],{type:e.type||"video/webm"})}function dN(){const e=[],o=new Set;let r,i,c=-1,l=0,p;const f=(y,w)=>{y!==i||c<0||o.has(c)||(o.add(c),e.push({CueTrack:y,CueClusterPosition:c,CueTime:Math.max(0,l+w)}))};return{cues:e,read:y=>{if(y.name==="TrackEntry"&&y.type==="m"){y.isEnd?(r?.type===1&&r.number!==void 0&&(i=r.number),r=void 0):r={};return}if(r&&y.name==="TrackNumber"&&typeof y.value=="number"){r.number=y.value;return}if(r&&y.name==="TrackType"&&typeof y.value=="number"){r.type=y.value;return}if(y.name==="Cluster"&&y.type==="m"&&!y.isEnd&&typeof y.tagStart=="number"){c=y.tagStart,l=0;return}if(y.name==="Timestamp"&&typeof y.value=="number"){l=y.value;return}if(y.name==="BlockGroup"&&y.type==="m"){y.isEnd?(p?.trackNumber!==void 0&&!p.hasReference&&f(p.trackNumber,p.relativeTimestamp),p=void 0):p={relativeTimestamp:0,hasReference:!1};return}if(p&&y.name==="ReferenceBlock"){p.hasReference=!0;return}if(y.name!=="Block"&&y.name!=="SimpleBlock")return;const w=uN(y.data);if(w){if(y.name==="Block"&&p){p.trackNumber=w.trackNumber,p.relativeTimestamp=w.relativeTimestamp;return}y.name==="SimpleBlock"&&w.keyframe&&f(w.trackNumber,w.relativeTimestamp)}}}}function uN(e){if(!e?.length)return;const o=pN(e[0],8);if(!o||e.length<o+3)return;let r=e[0]&(1<<8-o)-1;for(let p=1;p<o;p+=1)r=r*256+e[p];const i=e[o]<<8|e[o+1],c=i&32768?i-65536:i,l=e[o+2];return{trackNumber:r,relativeTimestamp:c,keyframe:!!(l&128)}}function pN(e,o){for(let r=1;r<=o;r+=1)if(e&1<<8-r)return r}function mN(e){const o=[e],r=new Set;for(;o.length>0;){const i=o.shift();if(!i||typeof i!="object"||r.has(i))continue;r.add(i);const c=i;if(typeof c.Decoder=="function"&&typeof c.Reader=="function"&&c.tools&&typeof c.tools.makeMetadataSeekable=="function")return c;const l=i;o.push(l.default,l.E)}}function hN(e){const o=e.type.toLowerCase();return o.includes("webm")||o.includes("matroska")}function gN(e){return(Number.isFinite(e)?Math.max(.25,e):.25)*1e3+220}const fN=300*1e3;function bN(e,o){return e&&!o}async function $m(e,o,r){if(r&&typeof navigator.share=="function"){const l=new File([e],o,{type:e.type||"application/octet-stream"});let p=!1;try{p=typeof navigator.canShare=="function"&&navigator.canShare({files:[l]})}catch{p=!1}if(p)try{return await navigator.share({files:[l]}),"shared"}catch(f){if(f instanceof Error&&f.name==="AbortError")return"cancelled"}}const i=URL.createObjectURL(e),c=document.createElement("a");return c.href=i,c.download=o,document.body.append(c),c.click(),c.remove(),window.setTimeout(()=>URL.revokeObjectURL(i),fN),"downloaded"}const yN=44,vN=2,gk=32e3,kN=25e5,fk=[32e3,24e3,22050,2e4,18e3,16e3];function bk(e){return SN(e,wN(e))}function wN(e){const o=e.samples.length/Math.max(1,e.sampleRate);if(!Number.isFinite(o)||o<=0)return Math.min(e.sampleRate,gk);const r=Math.max(1,Math.floor((kN-yN)/vN)),i=Math.floor(r/o),c=Math.min(e.sampleRate,gk,i),l=fk.at(-1);return c<l?Math.max(1,c):fk.find(p=>p<=c)??l}function SN(e,o){const r=Math.max(1,Math.round(o));if(!e.samples.length)return{samples:new Float32Array,sampleRate:r};if(e.sampleRate===r)return{samples:new Float32Array(e.samples),sampleRate:e.sampleRate};const i=e.sampleRate/r,c=Math.max(1,Math.round(e.samples.length/i)),l=new Float32Array(c);for(let p=0;p<c;p+=1){const f=p*i,b=Math.min(e.samples.length-1,Math.floor(f)),y=Math.min(e.samples.length-1,b+1),w=f-b;l[p]=e.samples[b]*(1-w)+e.samples[y]*w}return{samples:l,sampleRate:r}}const xN=48e3,RN=192e3,OS=-1;function US(){return 10**(OS/20)}function PN(e){let o=0;for(const l of e)o=Math.max(o,Math.abs(l));const r=US();if(o<=r||o===0)return e;const i=new Float32Array(e.length),c=r/o;for(let l=0;l<e.length;l+=1)i[l]=e[l]*c;return i}function yk(e,o){const r=e.createGain(),i=e.createDynamicsCompressor(),c=e.createGain();return i.threshold.value=OS,i.knee.value=0,i.ratio.value=20,i.attack.value=.003,i.release.value=.15,c.gain.value=US(),r.connect(i),i.connect(c),c.connect(o),r}function CN(e,o){return e?"mobile":o==="constrained"?"desktop-constrained":"desktop"}function MN(e){return/Edg(?:e|A|iOS)?\//i.test(e)?"edge":/(?:CriOS|Chrome|Chromium)\//i.test(e)?"chrome":/(?:FxiOS|Firefox)\//i.test(e)?"firefox":/Safari\//i.test(e)&&!/(?:Chrome|Chromium|CriOS|Android)\//i.test(e)?"safari":"other"}function Ih(){return window.matchMedia("(display-mode: standalone)").matches||!!navigator.standalone}function yt(){const e=navigator.userAgent,o=navigator.platform;return/iPhone|iPad|iPod/i.test(e)||o==="MacIntel"&&navigator.maxTouchPoints>1}function vk(){if(!yt())return;const e=window.AudioContext??window.webkitAudioContext;if(e)try{const o=new e;return o.resume().catch(()=>{}),o}catch{return}}async function kk(e){e&&e.state!=="closed"&&await e.close().catch(()=>{})}function jN(){try{return globalThis.crypto?.randomUUID?.()??`${Date.now()}-${Math.random().toString(36).slice(2)}`}catch{return`${Date.now()}-${Math.random().toString(36).slice(2)}`}}function Bt(){const e=navigator.userAgent,o=navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1,r=window.matchMedia?.("(pointer: coarse)").matches??!1,i=navigator.maxTouchPoints>0&&r&&Math.min(window.screen.width,window.screen.height)<=1024;return/Android|iPhone|iPad|iPod/i.test(e)||o||i}function wk(){const e=navigator.deviceMemory;return $I({mobile:Bt(),hardwareConcurrency:navigator.hardwareConcurrency,deviceMemoryGb:e})}function An(e){return e.length>1?e.replace(/\/+$/,""):e}function Sk(e){const o=An(e);return o==="/preview"?"preview":o==="/news"||o.startsWith("/news/")?"news":o==="/guide"||o.startsWith("/guide/")||o==="/docs"||o.startsWith("/docs/")?"guide":o==="/dub-maker"?"maker":o==="/support"?"support":"menu"}const TN=[{label:"About",href:"/about"},{label:"Terms",href:"/terms"},{label:"Privacy",href:"/privacy"},{label:"Copyright",href:"/copyright"},{label:"DMCA",href:"/dmca"}],EN="https://yours1.itch.io/voxalike",AN="https://discord.gg/7CdrrqNzF5",IN="https://www.reddit.com/r/Voxalike/",xk="https://voxalike.com",NN="https://www.tiktok.com/tiktokstudio/upload",DN="https://www.youtube.com/upload",LN={..._E,...YE}
156,VN={title:_v.seoTitle,description:_v.description,canonicalPath:"/"},zN={title:Ph.seoTitle,description:Ph.description,canonicalPath:"/news"};function FS(e){const o=An(e);return o==="/docs"||o.startsWith("/docs/")?Rh.find(r=>r.path===o)??Rh[0]:xh.find(r=>r.path===o)??xh[0]}function ON(e){const o=e.path==="/docs"||e.path.startsWith("/docs/");return e.sectionId?`${e.label} - ${o?"Voxalike Docs":"Voxalike Guide"}`:o?Mc.seoTitle:qi.seoTitle}function UN(e,o=window.location.pathname){if(e==="preview")return{title:"Voxalike Theme and Judge Preview",description:"Preview Voxalike visual themes and judge packs in a focused local test page.",canonicalPath:"/preview",robots:"noindex,nofollow"};if(e==="news")return zN;if(e==="guide"){const r=FS(o),i=gw(o,window.location.search);return{title:ON(r),description:LN[r.path]??(r.path.startsWith("/docs")?Mc.description:qi.description),canonicalPath:r.path,robots:i?"noindex,nofollow":void 0}}return e==="maker"?{title:"Dub Mod Maker - Create Voxalike Mods",description:"Create a Voxalike Dub Mod from an MP4 scene, mark spoken lines, add roles and captions, then export a ready-to-play ZIP.",canonicalPath:"/dub-maker",robots:"noindex,follow"}:e==="support"?{title:"Support Voxalike",description:"Support Voxalike once and unlock permanent ad-free access, a Supporter badge, name colors and avatar frames.",canonicalPath:"/support",robots:"noindex,follow"}:VN}function FN(e){return e==="/"?`${xk}/`:`${xk}${e}`}function jd(e,o){let r=document.querySelector(`meta[name="${e}"]`);r||(r=document.createElement("meta"),r.name=e,document.head.appendChild(r)),r.content=o}function Gm(e,o){let r=document.querySelector(`meta[property="${e}"]`);r||(r=document.createElement("meta"),r.setAttribute("property",e),document.head.appendChild(r)),r.content=o}function BN(e){let o=document.querySelector('link[rel="canonical"]');o||(o=document.createElement("link"),o.rel="canonical",document.head.appendChild(o)),o.href=e}function WN(e){const o=FN(e.canonicalPath);document.title=e.title,jd("description",e.description),jd("robots",e.robots??"index,follow"),jd("twitter:title",e.title),jd("twitter:description",e.description),Gm("og:title",e.title),Gm("og:description",e.description),Gm("og:url",o),BN(o)}function $N(e){return e==="preview"?"/preview":e==="news"?"/news":e==="guide"?"/guide":e==="maker"?"/dub-maker":e==="support"?"/support":"/"}const Bd=720,Wd=1280,GN=!1,qN=540,HN=960,JN=1280,_N=720,YN=360,KN=640,ZN=854,XN=480;function QN(e,o,r,i,c=!1){if(e==="shorts")return c?{width:YN,height:KN}:i?{width:qN,height:HN}:{width:Bd,height:Wd};if(!i&&!c)return{width:o,height:r};const l=c?ZN:JN,p=c?XN:_N,f=Math.min(1,l/o,p/r),b=y=>Math.max(2,Math.floor(y/2)*2);return{width:b(o*f),height:b(r*f)}}const eD={single:{duration:82,timing:76,energy:91,pitch:68,total:84,rating:"Preview Run",notes:["Theme and judge preview"]},multi:{duration:88,timing:81,energy:94,pitch:73,total:87,rating:"Multiplayer Preview",notes:["Theme and judge preview"]}},qm=.4,Xr=.6,tD=.45,oD=900,Hm=8e3,uc=2e4,Jm=12e3,Rk=3e4,Pk=5e3,Td=12,rD=2,Ck=8,aD=500,Mk=2e3,_m=4e3,sg="voxalike:mic-noise-floor-rms:v2",cg="voxalike:mic-calibration-applied:v2",nD="voxalike:mic-noise-floor-rms",iD="voxalike:mic-calibration-applied",BS="voxalike:mic-processing-mode:v1",Nh="voxalike:recording-sync-offsets:v1",lg="voxalike:active-room-session",WS="voxalike:recent-room-dubs:v1",jk="voxalike:contributor-notice-dismissed:v1",sD=6e4,cD=2*6e4,lD=5*6e4,$S="voxalike:original-guide-enabled",Tk="voxalike:original-guide-warning-shown",GS="voxalike:background-mix-volume",dD=.25;function uD(){try{return localStorage.getItem($S)==="1"}catch{return!1}}function pD(){try{const e=localStorage.getItem(GS);return e===null?dh:Zd(Number(e))}catch{return dh}}function mD(){try{return ov(localStorage)}catch{return ov()}}function hD(e,o,r,i){try{r0(localStorage,{masterVolume:e,sfxVolume:o,musicVolume:r,microphoneVolume:i})}catch{}}function gD(){try{const e=localStorage.getItem(sg),o=e===null?0:Number(e),r=Number.isFinite(o)&&o>=0?o:0,i=localStorage.getItem(cg)==="1"||r>0;return{noiseFloorRms:r,applied:i}}catch{return{noiseFloorRms:0,applied:!1}}}function fD(){try{const e=localStorage.getItem(BS);return e==="raw"||e==="clean"?e:"natural"}catch{return"natural"}}function Ek(e){try{const o=JSON.parse(localStorage.getItem(Nh)??"{}");return fu(Number(o[e||"default"]??0))}catch{return 0}}function bD(e,o){try{const r=JSON.parse(localStorage.getItem(Nh)??"{}"),i=r&&typeof r=="object"?r:{};localStorage.setItem(Nh,JSON.stringify({...i,[e||"default"]:fu(o)}))}catch{}}function Ak(){try{localStorage.removeItem(sg),localStorage.removeItem(cg),localStorage.removeItem(nD),localStorage.removeItem(iD)}catch{return}}function yD(e){try{const o={roomCode:e.roomCode,playerToken:e.playerToken,playerId:e.playerId,expiresAt:e.expiresAt,savedAt:Date.now()};localStorage.setItem(lg,JSON.stringify(o))}catch{return}}function pc(){try{const e=localStorage.getItem(lg);if(!e)return;const o=JSON.parse(e),r=typeof o.expiresAt=="string"?o.expiresAt:"";if(typeof o.roomCode!="string"||typeof o.playerToken!="string"||typeof o.playerId!="str
156ing"||!r||Date.parse(r)<=Date.now()){du();return}return{roomCode:o.roomCode,playerToken:o.playerToken,playerId:o.playerId,expiresAt:r,savedAt:Number(o.savedAt)||0}}catch{du();return}}function du(){try{localStorage.removeItem(lg)}catch{return}}function vD(){try{const e=JSON.parse(localStorage.getItem(WS)??"[]");return VS(e.filter(o=>o?.game))}catch{return[]}}function Ym(e){const o=VS(e);try{localStorage.setItem(WS,JSON.stringify(o))}catch{}return o}function Dh(e=4.8){const r=new Float32Array(Math.round(4e3*e));for(let i=0;i<r.length;i+=1){const c=i/4e3,l=Math.max(.08,Math.sin(Math.PI*(c%1.15/1.15)));r[i]=Math.sin(c*Math.PI*2*115)*l*.55}return{samples:r,sampleRate:4e3}}function uu(e=4.8){return{duration:e,voicedDuration:e*.78,sampleRate:4e3,pitchContour:[.28,.36,.52,.44,.66,.58,.72,.49],pitchConfidence:.9,energyEnvelope:[.12,.38,.7,.55,.82,.64,.9,.42,.18],pausePositions:[1.45,3.1],onsetTimes:[.2,1.7,3.25],peakTimes:[.8,2.4,3.9],voicedRatio:.78,rms:.24,peak:.88}}function $d(e=88){return{duration:Math.max(0,e-4),timing:Math.max(0,e-8),energy:Math.min(100,e+5),pitch:Math.max(0,e-2),total:e,rating:e>=90?"Outstanding":"Great Take",notes:["Responsive UI fixture"]}}function kD(){const e=["Oh my god, I'm going to lose!","Turn around, every now and then I get a little bit nervous.","This line is intentionally longer so subtitle wrapping can be checked at every screen size.","Waitâare we really doing this right now?","Everybody stay calm. I have a plan.","That was perfect. Let's keep going!"],o=["Speed","Alex","Morgan","Yasmine","Killernine","Jordan"],r=e.map((i,c)=>({id:`ui-lab-clip-${c+1}`,title:`UI Lab Clip ${c+1}`,caption:i,speakers:[o[c]],audioFile:new Blob([],{type:"audio/wav"}),audioUrl:"/1.mp3",imageFile:new Blob([],{type:"image/png"}),imageUrl:c%2===0?"/menu-cover.png":"/guide/manage-dub-packs.png",duration:4.8+c*.2,referenceFeatures:uu(4.8+c*.2),metadata:{fixture:"responsive-ui-lab"}}));return{id:"ui-lab-pack",title:"Responsive Test Dub Mod",creator:"Voxalike UI Lab",language:"English",tags:["testing"],characters:o,iconFile:new Blob([],{type:"image/png"}),iconUrl:"/menu-cover.png",source:"local-zip",clips:r,backingTrackUrl:"/home-music.mp3",warnings:[],release:()=>{}}}function wD(e){const o=e.clips[0],r=Dh(o.duration);return{data:{roundId:"ui-lab-round",roundToken:"ui-lab-token",clip:{title:o.title,caption:o.caption,duration:o.duration??4.8,sourceLabel:e.title},expiresAt:"2030-01-01T00:00:00.000Z"},sessionId:"ui-lab-session",execution:"local",speakerNames:o.speakers,referenceAudioUrl:o.audioUrl,referenceImageUrl:o.imageUrl,referenceAudio:r,referenceFeatures:o.referenceFeatures,localReferenceFeatures:o.referenceFeatures}}const Ik=["Freja","Hazard","Juno","Venture","Mauga","Illari","Lifeweaver","Ramattra","Kiriko","Junker Queen","Sojourn","Echo","Sigma","Baptiste","Ashe","Wrecking Ball","Brigitte","Moira","Doomfist","Orisa","Sombra","Ana","Genji","Mei","D.Va","Junkrat","Roadhog","Lucio","Soldier: 76","Cassidy","Zarya","Symmetra","Zenyatta","Winston","Hanzo","Torbjorn","Mercy","Reinhardt","Pharah","Widowmaker","Reaper","Tracer"];function Wa(e,o){const r=Date.now(),i=[{id:"ui-host",moderationUserId:"ui-host-user",name:"Yasmine",isHost:!0,isReady:!0,joinedAt:new Date(r-9e4).toISOString(),lastSeenAt:new Date(r).toISOString()},{id:"ui-self",moderationUserId:"ui-self-user",name:"Killernine",isHost:!1,isReady:!0,joinedAt:new Date(r-7e4).toISOString(),lastSeenAt:new Date(r).toISOString()},{id:"ui-friend",moderationUserId:"ui-friend-user",name:"Sidulz",isHost:!1,isReady:o!=="lobby",joinedAt:new Date(r-4e4).toISOString(),lastSeenAt:new Date(r).toISOString()}],c={packId:e.id,packTitle:e.title,packSource:"local",creator:e.creator,clipCount:e.clips.length,videoCount:0,characters:e.characters};if(o==="lobby")return{roomId:"ui-lab-room",roomCode:"LAB1",status:"lobby",hostPlayerId:"ui-host",playerId:"ui-self",playerToken:"ui-lab-player-token",visibility:"public",players:i,lobbyPack:c,createdAt:new Date(r-9e4).toISOString(),expiresAt:new Date(r+36e5).toISOString(),lobbyDeadlineAt:new Date(r+12*6e4).toISOString(),recentResults:[]};const l=o==="results",p=l?"pvp":"coop",f=l?e.clips.slice(0,3).flatMap((w,M)=>i.map(E=>({index:M,clipId:w.id,title:w.title,caption:w.caption,playerId:E.id,playerName:E.name}))):e.clips.map((w,M)=>({index:M,clipId:w.id,title:w.title,caption:w.caption,playerId:M===0&&o==="waiting"?"ui-host":M%2===0?"ui-self":"ui-host",playerName:M===0&&o==="waiting"?"Yasmine":M%2===0?"Killernine":"Yasmine"})),b=l?f.map((w,M)=>{const z=92-M%i.length*6-Math.floor(M/i.length)*2;return{assignmentIndex:M,clipId:w.clipId,playerId:w.playerId??"ui-self",playerName:w.playerName??"Player",audioDataUrl:"data:audio/wav;base64,UklGRg==",score:$d(z),scoreTotal:z,rating:"Great Take",createdAt:new Date(r-2e4+M*500).toISOString()}}):[],y={packId:e.id,packTitle:e.title,packSource:"local",clipCount:l?3:e.clips.length,mode:p,assignmentMode:o==="claim-host"?"claim":"round-robin",allowRetakes:!0,currentAssignmentIndex:l?f.length:0,assignments:f,takes:b,startedAt:new Date(r-(o==="preparing"?2e3:45e3)).toISOString(),completedAt:l?new Date(r-5e3).toISOString():void 0};return{roomId:"ui-lab-room",roomCode:"LAB1",status:"playing",hostPlayerId:"ui-host",playerId:o==="claim-host"?"ui-host":"ui-self",playerToken:"ui-lab-player
156-token",players:i,lobbyPack:c,game:y,createdAt:new Date(r-9e4).toISOString(),expiresAt:new Date(r+36e5).toISOString(),resultDeadlineAt:l?new Date(r+10*6e4).toISOString():void 0,recentResults:[]}}function SD(e){const o=kD(),r=wD(o),i=Dh(),c=uu(),l=$d(91),p=e.startsWith("multi-"),f=e==="single-recording"||e==="multi-recording",b=e==="single-review"||e==="single-clip-picker"||e==="multi-review",y=["single-premiere","single-judge","single-results","single-details"].includes(e),w=["single-reference","single-ready","single-recording","single-review","single-clip-picker"].includes(e),M=["multi-waiting","multi-shared-take","multi-ready","multi-claim-host","multi-recording","multi-review"].includes(e),E=Object.fromEntries(o.clips.map((D,I)=>[D.id,{clipId:D.id,playerUrl:"data:audio/wav;base64,UklGRg==",playerAudio:Dh(D.duration),playerFeatures:uu(D.duration),score:$d(94-I*3)}])),z={[o.clips[0].id]:{clipId:o.clips[0].id,playerUrl:"data:audio/wav;base64,UklGRg==",playerAudio:i,playerFeatures:c,score:l}},N={id:"ui-lab-work",packId:o.id,packTitle:o.title,createdAt:Date.now()-1e4,clips:o.clips,takes:Object.fromEntries(Object.entries(E).map(([D,I])=>[D,I])),score:$d(89),completedCount:o.clips.length,backingTrackUrl:o.backingTrackUrl};let j;if(e==="multi-lobby"&&(j=Wa(o,"lobby")),e==="multi-role-selection"&&(j=Wa(o,"lobby"),j.playerId=j.hostPlayerId,j.roleSelectionActive=!0,j.lobbyPack={...j.lobbyPack,characters:Ik,roleClaimEnabled:!0},j.roleClaims=Ik.slice(0,6).map(D=>({role:D,playerId:j.playerId,playerName:j.players.find(I=>I.id===j.playerId)?.name??"Host"}))),e==="multi-preparing"&&(j=Wa(o,"preparing")),(e==="multi-waiting"||e==="multi-shared-take")&&(j=Wa(o,"waiting"),j.takePreviewSupported=!0,e==="multi-shared-take"&&j.game)){const D=j.game.assignments[j.game.currentAssignmentIndex];j.takePreview={previewId:"ui-lab-shared-take",turnKey:$t(j.game),runKey:j.game.startedAt,assignmentIndex:j.game.currentAssignmentIndex,clipId:D.clipId,playerId:D.playerId,playerName:D.playerName,audioDataUrl:Tn(i),createdAt:new Date().toISOString()}}return["multi-ready","multi-recording","multi-review"].includes(e)&&(j=Wa(o,"mine")),e==="multi-claim-host"&&(j=Wa(o,"claim-host")),e==="multi-syncing"&&(j=Wa(o,"syncing")),e==="multi-results"&&(j=Wa(o,"results")),{id:e,view:p?"multi":"single",pack:o,round:w||M?r:void 0,room:j,recordedTakes:y?E:b?z:{},completedWorks:y?[N]:[],startingSingleRun:e==="single-preparing",packRunActive:w||M||e==="multi-preparing"||e==="multi-syncing",packRunComplete:y||e==="multi-results",recording:f,preparingRoomTurn:e==="multi-preparing",singleStageControlsVisible:e!=="single-reference",singleClipPickerOpen:e==="single-clip-picker",showResultDetails:e==="single-details",dubPremiereComplete:e!=="single-premiere",judgeScoreShowComplete:!["single-premiere","single-judge"].includes(e),waveformProgress:f?.54:b?1:e==="single-reference"?.42:0,recordingElapsedSeconds:f?2.8:0,liveRecordingLevels:f?Array.from({length:34},(D,I)=>({progress:I/33,value:.18+I*17%11/15})):[],recoverableDubDraft:e==="single-recovery"?{id:"ui-lab-recovery",schemaVersion:1,mode:"single",status:"active",sourceId:"ui-lab-pack",packId:o.id,packTitle:o.title,createdAt:Date.now()-3e5,updatedAt:Date.now()-24e4,currentClipIndex:0,clipSequence:o.clips.map((D,I)=>I)}:void 0,status:f?"Recording your take.":b?"Take ready. Replay it or continue.":"Stage ready."}}function xD(){const{locale:e,setLocale:o,t:r}=Be(),i=void 0,l=m.useRef(i?SD(i):void 0).current,p=!!l,f=m.useRef(gD()),{snapshot:b,requestPermission:y,selectDevice:w,validateConnection:M,invalidateConnection:E}=JA(),{activeTheme:z,activeThemeId:N,deleteTheme:j,enableTheme:D,getThemeAsset:I,getThemePreviewAsset:B,importTheme:L,ready:_,themes:oe,useDefaultTheme:W}=Gh(),[O,K]=m.useState(()=>An(window.location.pathname)),[T,Y]=m.useState(()=>l?.view??Sk(window.location.pathname)),[X,me]=m.useState(()=>sh()&&sessionStorage.getItem(jk)!=="1");m.useEffect(()=>{if(!X)return;let a;const n=()=>
156{if(window.clearTimeout(a),!sh()){me(!1);return}a=window.setTimeout(n,Math.min(Pw-Date.now()+1,6e4))};return document.addEventListener("visibilitychange",n),window.addEventListener("pageshow",n),n(),()=>{window.clearTimeout(a),document.removeEventListener("visibilitychange",n),window.removeEventListener("pageshow",n)}},[X]);const[We,Ae]=m.useState(),[vt,kt]=m.useState(Ih),[be,ce]=m.useState({rms:0,peak:0}),[pe,Ie]=m.useState(),Ht=m.useRef(void 0),[q,co]=m.useState(l?.round),ke=m.useRef(q),te=a=>{ke.current=a,co(a)},[ae,It]=m.useState(l?.recording??!1),[ut,lo]=m.useState(!1),[ta,uo]=m.useState(!1),[Xa,oa]=m.useState(),[vr,To]=m.useState(),[wt,ra]=m.useState(!1),[Eo,po]=m.useState("idle"),[he,mo]=m.useState(),[Ee,Ao]=m.useState(fD),[Jt,ct]=m.useState(()=>Ek(b.selectedDeviceId)),[ze,aa]=m.useState(f.current.noiseFloorRms),[kr,Fn]=m.useState(f.current.applied),[ho,Bn]=m.useState(!1),[wr,Wn]=m.useState(),[Zo,$n]=m.useState("loading"),[Ki,na]=m.useState(1),[ia,Sr]=m.useState(!1),[Xo,Zi]=m.useState(!1),[Io,sa]=m.useState(0),[Xi,Vc]=m.useState(!1),[ca,xr]=m.useState(),[V,Z]=m.useState(!1),[ne,Se]=m.useState(),[Nt,Me]=m.useState(!1),[Oe,Rr]=m.useState(()=>l&&["single-review","single-clip-picker","multi-review"].includes(l.id)?uu():void 0),[Ce,_t]=m.useState(),[Ke,la]=m.useState(),[zc,P]=m.useState(l?.status??"Microphone setup pending."),[Qa,xe]=m.useState(),[No,Oc]=m.useState(hI),[H,Qi]=m.useState(l?.pack),[Le,es]=m.useState([]),[Yt,Pr]=m.useState([]),[pt,Do]=m.useState([]),[ts,en]=m.useState([]),[tn,Cr]=m.useState([]),[Su,Uc]=m.useState(!1),[Fc,pg]=m.useState(!1),[Mr,xu]=m.useState(!1),[Bc,Ru]=m.useState(0),[Gn,mg]=m.useState(!1),[Wc,hg]=m.useState(!1),[$c,Re]=m.useState(""),[Gc,gg]=m.useState(),[Pu,fg]=m.useState(),[bg,yg]=m.useState(),[go,qn]=m.useState({}),[da,qc]=m.useState(()=>new Set),[os,vg]=m.useState(()=>new Set),[kg,wg]=m.useState(()=>new Set),[ZS,XS]=m.useState({}),[QS,ex]=m.useState({}),[tx,Sg]=m.useState({}),[xg,Rg]=m.useState(),[Pg,Cg]=m.useState(""),[Cu,on]=m.useState("dub"),[rs,Mu]=m.useState(""),[Hc,Qo]=m.useState(!1),[Mg,Hn]=m.useState(),[ee,Jn]=m.useState(null),[Ve,jg]=m.useState(!0),[Tg,ox]=m.useState("aura"),[er,rx]=m.useState([]),[Eg,ju]=m.useState(!1),[ax,Ag]=m.useState(),[nx,Ig]=m.useState(!1),[ix,Ng]=m.useState(()=>new URLSearchParams(window.location.search).get("gifts")==="open"),[as,Dg]=m.useState("account"),[_n,Tu]=m.useState(1),[Lg,sx]=m.useState(""),[jr,Eu]=m.useState(),[Vg,zg]=m.useState(!1),[Og,Au]=m.useState(""),[cx,lx]=m.useState(!1),rn=m.useRef(new Map),Jc=m.useRef(void 0),Iu=m.useRef(new Set),[,dx]=m.useState(0),Ug=m.useRef(null),Fg=m.useRef(null),Bg=m.useRef(null),_c=m.useRef(null),[Wg,fo]=m.useState(""),[Nu,ua]=m.useState(),[Dt,ux]=m.useState(),[pa,px]=m.useState([]),[$g,Kt]=m.useState(""),[mx,tr]=m.useState([]),[ma,ha]=m.useState(!1),an=m.useRef(void 0),Yc=m.useRef([]),[Du,hx]=m.useState("single"),[Gg,Lu]=m.useState("setup"),[gx,ns]=m.useState("menu"),[ye,nn]=m.useState(),[Tr,ga]=m.useState(),qg=m.useRef(F0()),Hg=m.useRef(!1),[is,Jg]=m.useState(qg.current),[Vu,fa]=m.useState(""),[Yn,fx]=m.useState("private"),[zu,bx]=m.useState(e),[yx,vx]=m.useState([]),[kx,_g]=m.useState(!1),[wx,Yg]=m.useState(""),[Ou,Sx]=m.useState("all"),[Uu,xx]=m.useState("lobby"),[v,Ne]=m.useState(l?.room),[Kc,Kg]=m.useState(4),[Fu,Zc]=m.useState([]),Zg=m.useRef(new Set),[Bu,Kn]=m.useState(""),[ss,Wu]=m.useState(vD),[$u,cs]=m.useState(!1),[sn,Xc]=m.useState(),[Rx,ls]=m.useState(),Xg=m.useRef(new Set),[Er,cn]=m.useState(!1),[Qg,Qc]=m.useState(),[Px,G]=m.useState("Create or join a room."),[ds,Gu]=m.useState(""),[mt,us]=m.useState("coop"),[qu,ef]=m.useState("turns"),[ba,ps]=m.useState("round-robin"),[el,tf]=m.useState(!1),[tl,of]=m.useState(!1),[Cx,rf]=m.useState(""),[or,Lo]=m.useState(),[Ar,Hu]=m.useState(!1),[Mx,af]=m.useState(),[ya,ol]=m.useState(!1),[Ju,nf]=m.useState(!1),[sf,cf]=m.useState(),[ln,lf]=m.useState(!1),[rl,df]=m.useState(!1),[_u,uf]=m.useState(!1),[Yu,pf]=m.useState(!1),[mf,Zn
156]=m.useState(l?.id==="multi-claim-host"),[Ku,Zu]=m.useState(!1),[Xu,hf]=m.useState(!1),[jx,Qu]=m.useState(),[gf,al]=m.useState(l?.preparingRoomTurn??!1),[St,bo]=m.useState(),[Tx,ep]=m.useState(!1),[xt,va]=m.useState(0),[ff,Xn]=m.useState([]),[tt,Zt]=m.useState(l?.packRunActive??!1),[ge,Lt]=m.useState(l?.packRunComplete??!1),[rr,ka]=m.useState(l?.startingSingleRun??!1),[yo,Ir]=m.useState(l?.showResultDetails??!1),[Rt,Qn]=m.useState(l?.dubPremiereComplete??!1),[Ex,dn]=m.useState(!1),[ms,Nr]=m.useState(l?.judgeScoreShowComplete??!1),[hs,vo]=m.useState(l?.singleStageControlsVisible??!1),[ve,Vt]=m.useState(!1),[Ax,gs]=m.useState(l?.singleClipPickerOpen??!1),[Ix,un]=m.useState(),[pn,tp]=m.useState(!1),[nl]=m.useState(mD),[$e,Nx]=m.useState(nl.masterVolume),[Dr,Dx]=m.useState(nl.sfxVolume),[Lr,Lx]=m.useState(nl.musicVolume),[mn,Vx]=m.useState(nl.microphoneVolume),[hn,zx]=m.useState(pD),[fs,il]=m.useState(!1),[ar,Ox]=m.useState(uD),ei=m.useRef(null),ti=m.useRef(null),wa=m.useRef(null),op=m.useRef(null),rp=m.useRef(null),ap=m.useRef(null),np=m.useRef(jN()),ip=m.useRef({type:"background-music-claim",ownerId:np.current,activatedAt:0}),sp=m.useRef(!0),bs=m.useRef(!0),[bf,cp]=m.useState(()=>!document.hidden),[yf,vf]=m.useState(!0),[ko,Vo]=m.useState(l?.recordedTakes??{}),[ys,gn]=m.useState(l?.completedWorks??[]),[oi,sl]=m.useState(l?.recoverableDubDraft),[Ux,kf]=m.useState([]),[vs,cl]=m.useState(!1),[Fx,ot]=m.useState(l?.waveformProgress??0),[qe,Sa]=m.useState(!1),[ks,nr]=m.useState(!1),[Bx,wf]=m.useState(l?.liveRecordingLevels??[]),[Sf,ll]=m.useState(l?.recordingElapsedSeconds??0),He=m.useRef(void 0),ri=m.useRef(!1),ws=m.useRef("idle"),lp=m.useRef(void 0),ai=m.useRef(void 0),dl=m.useRef({}),ul=m.useRef(null),fe=m.useRef(null),Pt=m.useRef(null),ir=m.useRef(null),xf=m.useRef(void 0),Xt=m.useRef(void 0),fn=m.useRef(void 0),[ni,Rf]=m.useState(),[Ss,pl]=m.useState("idle"),xa=m.useRef(void 0),dp=m.useRef(0),ml=m.useRef(void 0),zo=m.useRef(null),ht=m.useRef(null),Ra=m.useRef(hn),Pf=m.useRef(null),Cf=m.useRef(null),je=m.useRef({}),Mf=m.useRef(""),Te=m.useRef(void 0),up=m.useRef(Promise.resolve()),jf=m.useRef(""),gt=m.useRef(0),Oo=m.useRef(0),Tf=m.useRef(Promise.resolve()),hl=m.useRef(new Map),Vr=m.useRef(new Set),xs=m.useRef(new Set),ii=m.useRef(""),Rs=m.useRef(0),pp=m.useRef(0),gl=m.useRef(0),mp=m.useRef(""),fl=m.useRef(void 0),bl=m.useRef(void 0),Pa=m.useRef(void 0),yl=m.useRef(void 0),vl=m.useRef([]),sr=m.useRef(!1),Ef=m.useRef(0),Af=m.useRef(1),kl=m.useRef(0),hp=m.useRef(0),cr=m.useRef(!1),si=m.useRef(void 0),wl=m.useRef(null),gp=m.useRef([]),If=m.useRef([]),Nf=m.useRef([]),bn=m.useRef({}),Sl=m.useRef({}),Ca=m.useRef({}),Ps=m.useRef({}),xl=m.useRef(void 0),ci=m.useRef({}),Rl=m.useRef({}),zr=m.useRef({}),li=m.useRef({}),Pl=m.useRef({}),Or=m.useRef(void 0),Cs=m.useRef(Promise.resolve()),Ur=m.useRef({}),wo=m.useRef(""),zt=m.useRef(""),Cl=m.useRef(""),Ue=m.useRef({}),Ms=m.useRef(void 0),yn=m.useRef({}),di=m.useRef(!1),ui=m.useRef(!1),Fr=m.useRef({}),rt=m.useRef({}),Uo=m.useRef(!1),Qt=m.useRef(void 0),fp=m.useRef(!1),Ml=m.useRef(!1),bp=m.useRef(0),jl=m.useRef(0),lr=m.useRef(!1),Br=m.useRef(!1),dr=m.useRef(!1),yp=m.useRef(""),Tl=m.useRef(void 0),Ma=m.useRef(""),eo=m.useRef(""),Wr=m.useRef(""),So=m.useRef(""),El=m.useRef(""),Al=m.useRef(""),vp=m.useRef(null),Il=m.useRef(new Map),kp=m.useRef(new Set),js=m.useRef(void 0),Ts=m.useRef(void 0),wp=m.useRef(0),pi=m.useRef(void 0),Ze=m.useRef(void 0),Sp=m.useRef(!1),Ct=m.useRef(void 0),Nl=m.useRef(!1),Es=m.useRef(0),Dl=m.useRef(!1),As=m.useRef(0),mi=m.useRef(0),xp=m.useRef(0),lt=m.useRef(void 0),Df=m.useRef(()=>{}),Rp=m.useRef(Promise.resolve()),ja=m.useRef(!1),Pp=m.useRef(""),Cp=m.useRef(0),xo=m.useRef(0),Ll=m.useRef(void 0),Is=m.useRef(0);ke.current=q,m.useEffect(()=>{const a=()=>{K(An(window.location.pathname)),Y(Sk(window.location.pathname))};return window.addEventListener("popstate",a),()=>{window.removeEventListener("popstate",a),_r(!1),He.current?.cancel()}},[]),m.useEffect(()=>{if(!$u)return;const a=n=>{n.key==="Escape"&&cs(!1)};return window.addEventListener("keydown",a),()=>window.removeEventListener("keydown",a)},[$u]),m.useEffect(()=>{const a=window.matchMedia("(display-mode: standalone)"),n=()=>kt(Ih()),s=u=>{u.preventDefault(),Ae(u)},d=()=>{Ae(void 0),kt(!0)};return window.addEventListener("beforeinstallprompt",s),window.addEventListener("appinstalled",d),a.addEventListener("change",n),(
156)=>{window.removeEventListener("beforeinstallprompt",s),window.removeEventListener("appinstalled",d),a.removeEventListener("change",n)}},[]),m.useEffect(()=>{Ve||Hg.current||Jg(W0(ee?.displayName,qg.current))},[Ve,ee]),m.useEffect(()=>{WN(UN(T,O))},[O,T]),m.useEffect(()=>{if(!Xi)return;const a=n=>{n.preventDefault(),n.returnValue=""};return window.addEventListener("beforeunload",a),()=>window.removeEventListener("beforeunload",a)},[Xi]),m.useEffect(()=>{let a=!1;return jg(!0),gd().then(n=>{a||Jn(n)}).catch(()=>{a||Jn(null)}).finally(()=>{a||jg(!1)}),()=>{a=!0}},[]),m.useEffect(()=>{T!=="support"||Ve||!ee||!ee.supporterCheckoutVisible||SM(ee.id)},[Ve,ee?.id,ee?.supporterCheckoutVisible,T]),m.useEffect(()=>{if(!(O==="/support"&&new URLSearchParams(window.location.search).get("payment")==="return")||!ee||ee.supporterIdentities.includes("supporter"))return;let n=!1,s=0,d;const u=()=>{n||document.hidden||s>=20||(s+=1,gd().then(h=>{n||!h||h.id!==ee.id||(Jn(h),!h.supporterIdentities.includes("supporter")&&s<20&&(d=window.setTimeout(u,2500)))}).catch(()=>{!n&&s<20&&(d=window.setTimeout(u,2500))}))};return u(),()=>{n=!0,window.clearTimeout(d)}},[ee?.id,ee?.supporterIdentities.includes("supporter"),O]),m.useEffect(()=>{if(T!=="support"||Ve||!ee)return;const a=new URLSearchParams(window.location.search),n=a.get("payment")==="gift_return"?a.get("order"):null;let s=!1,d=0,u;ju(!0);const h=()=>{s||document.hidden||d>=20||(d+=1,bM().then(x=>{if(s)return;rx(x),n&&!x.some(S=>S.orderId===n)&&d<20?u=window.setTimeout(h,2500):ju(!1)}).catch(()=>{!s&&n&&d<20?u=window.setTimeout(h,2500):s||ju(!1)}))};return h(),()=>{s=!0,window.clearTimeout(u)}},[Ve,ee?.id,O,T]),m.useEffect(()=>{if(!(O==="/support"&&new URLSearchParams(window.location.search).get("payment")==="gift_sent")||!ee)return;const n=new URLSearchParams(window.location.search).get("order");if(!n)return;let s=!1,d=0,u;const h=()=>{s||document.hidden||d>=20||(d+=1,vM().then(x=>{s||(x.some(g=>g.id===n&&g.status==="paid")?(lx(!0),gd().then(g=>{!s&&g?.id===ee.id&&Jn(g)})):d<20&&(u=window.setTimeout(h,2500)))}).catch(()=>{!s&&d<20&&(u=window.setTimeout(h,2500))}))};return h(),()=>{s=!0,window.clearTimeout(u)}},[ee?.id,O]),m.useEffect(()=>{if(!ee?.isAdmin)return;let a=!1,n=!1;const s=ee.id,d=()=>{a||n||document.hidden||(n=!0,gd().then(h=>{!a&&h?.id===s&&Jn(h)}).catch(()=>{}).finally(()=>{n=!1}))},u=window.setInterval(d,sD);return window.addEventListener("focus",d),document.addEventListener("visibilitychange",d),()=>{a=!0,window.clearInterval(u),window.removeEventListener("focus",d),document.removeEventListener("visibilitychange",d)}},[ee?.id,ee?.isAdmin]),m.useEffect(()=>{je.current=ko},[ko]),m.useEffect(()=>()=>{Pa.current!==void 0&&window.cancelAnimationFrame(Pa.current)},[]),m.useEffect(()=>{if(!tt||T!=="single"&&T!=="multi"||!H)return;const a=H.clips[xt];gI({mode:T,phase:ae?"recording":ve?"advancing-clip":q?"stage-ready":"preparing-stage",modId:H.id,modTitle:H.title,clipId:a?.id,clipTitle:a?.title,clipIndex:xt,clipCount:H.clips.length})},[ve,xt,q,H,tt,ae,T]),m.useEffect(()=>{b.status!=="disconnected"||!sr.current||(_r(!1),He.current?.cancel(),He.current=void 0,cr.current=!1,fe.current?.pause(),It(!1),P("Microphone disconnected. Reconnect it, then record this line again."))},[b.status]),m.useEffect(()=>{if(p)return;let a=!1;return Od().then(async n=>{const s=n.filter(u=>u.mode==="multi"&&Date.now()-u.updatedAt<=Bh),d=(await Promise.all(s.map(async u=>(await fc(u.id).catch(()=>[])).length?u:void 0))).filter(u=>!!u);a||(sl(n.find(u=>u.mode==="single")),kf(d))}).catch(()=>{}),()=>{a=!0}},[v?.roomCode,p,T]),m.useEffect(()=>()=>{qr(),_r(!1),Ri(),Ls(),Vs(),Dp(),wn(),Ob(je.current),Ct.current&&window.clearTimeout(Ct.current),Qt.current&&window.clearTimeout(Qt.current)},[]),m.useEffect(()=>{if(p){Sp.current=!0;return}if(!Gn||Sp.current)return;Sp.current=!0;const n=new URLSearchParams(window.location.search).get("room")?.toUpperCase(),s=pc();if(n&&(fa(n),Y("multi")),s&&(!n||s.roomCode===n)){Bl(s);return}n&&G("Room code ready. Enter your name and join.")},[Gn]),m.useEffect(()=>()=>{js.current&&window.clearTimeout(js.current),Ts.current&&window.clearTimeout(Ts.current)},[]),m.useEffect(()=>()=>H?.release(),[H]),m.useEffect(()=>()=>{rn.current.forEach(a=>URL.revokeObjectURL(a)),rn.current.clear()},[]),m.useEffect(()=>{if(ei.current&&(ei.current.volume=$e*Dr),ti.current&&(ti.current.volume=$e*Dr),wa.current){const a=uh($e,Lr),n=wa.current,s=rp.current;n.muted=a===0,n.volume=s?1:a,s&&(s.gain.value=a),a===0&&n.pause()}},[$e,Lr,Dr]),m.useEffect(()=>{hD($e,Dr,Lr,mn)},[$e,mn,Lr,Dr]),m.useEffect(()=>{Ra.current=hn,ht.current&&(ht.current.volume=Rm($e,hn));try{localStorage.setItem(GS,String(hn))}catch{}},[hn,$e]),m.useEffect(()=>{if(!fs)return;const a=s=>{(!(s.target instanceof Node)||!Pf.current?.contains(s.target))&&il(!1)},n=s=>{s.key==="Escape"&&il(!1)};return document.addEventListener("pointerdown",a),document.addEventListener("keydown",n),()=>{document.removeEventListener("pointerdown",a),document.removeEventListener("keydown",n)}},[fs]),m.useEffect(()=>{if(!Hc)return;const a=s=>{(!(s.target instanceof Node)||!Cf.current?.contains(s.target))&&Qo(!1)},n=s=>{s.key==="Escape"&&Qo(!1)};return document.addEventListener("pointerdown",a),document.addEventListener("keydown",n),()=>{document.removeEventListener("pointerdown",a),document.removeEventListener("keydown",n)}},[Hc]),m.useEffect(()=>{const a=()=>{bs.current=!1,cp(!1),wa.current?.pause()},n=()=>
156{if(!!document.hidden){a();return}cp(!0),jp()},s=()=>{document.hidden||(cp(!0),jp())},d=()=>a();let u;return typeof BroadcastChannel<"u"&&(u=new BroadcastChannel("voxalike:background-music:v1"),ap.current=u,u.addEventListener("message",h=>{if(!o0(h.data)||h.data.ownerId===np.current)return;t0(h.data,ip.current)>0?(sp.current=!1,bs.current=!1,vf(!1),wa.current?.pause()):sp.current&&!document.hidden&&u?.postMessage(ip.current)})),document.addEventListener("visibilitychange",n),window.addEventListener("focus",s),window.addEventListener("blur",d),document.hidden||jp(),()=>{document.removeEventListener("visibilitychange",n),window.removeEventListener("focus",s),window.removeEventListener("blur",d),ap.current=null,u?.close()}},[]),m.useEffect(()=>{const a=!rr&&!ya&&!tt&&!ge&&!v?.game,n=e0({routeAllowsMusic:a,masterVolume:$e,musicVolume:Lr,microphoneConnecting:ut,micCheckActive:wt,recording:ae,pageVisible:bf,ownsPlayback:yf});bs.current=n;const s=wa.current;if(!s)return;if(!n){s.pause();return}const d=()=>{s.preload="auto",s.readyState===HTMLMediaElement.HAVE_NOTHING&&s.load(),qx().then(()=>s.play()).catch(()=>{})};if(document.documentElement.classList.contains("app-ready")){d();return}return window.addEventListener("voxalike:app-ready",d,{once:!0}),()=>window.removeEventListener("voxalike:app-ready",d)},[wt,$e,ut,Lr,yf,bf,tt,ge,ae,v?.game,ya,rr]),m.useEffect(()=>{const a=Rn(),n=a.find(s=>s.id===Tr)??a.find(s=>s.id===ye)??a[0];n&&(n.id===ye&&H?.iconUrl||rn.current.has(n.id)||JP(n))},[Tr,ye,Le,H?.iconUrl,da]),m.useEffect(()=>{if(p){mg(!0);return}let a=!1;const n=++gt.current;async function s(){const d=await nk(),u=Xm(d);!a&&n===gt.current&&es(u);const h=await XA()??u[0];if(!h||a||n!==gt.current)return;P(`Restoring ${h.name}.`);const x=Fm(h);if(x.length)try{const g=h.kind==="zip"?await bc(x[0]):await Em(x);if(a||n!==gt.current){g.release();return}const S={...Bo(g),workshopId:h.summary?.workshopId,workshopSlug:h.summary?.workshopSlug,workshopPackageHash:h.summary?.workshopPackageHash,workshopUpdatedAt:h.summary?.workshopUpdatedAt};await ak({...h,summary:S}).catch(()=>{}),nn(h.id),ga(h.id),Hs({...h,summary:S}),Mi(g,Yr(g,"Restored for this refresh."))}catch{!a&&n===gt.current&&P("Saved dub mod could not be restored. Import it again.")}}return s().catch(()=>{a||P("Saved dub mods could not be restored.")}).finally(()=>{a||mg(!0)}),()=>{a=!0}},[]),m.useEffect(()=>{if(p){hg(!0);return}let a=!1;return Promise.all([hA(),Zv()]).then(([n,s])=>
156{if(a){Ba(n),s.forEach(Ba);return}Ei(n),yy(s)}).catch(()=>{a||Kt("Saved judge pack could not be restored.")}).finally(()=>{a||hg(!0)}),()=>{a=!0,Ba(an.current),Yc.current.forEach(Ba),Yc.current=[],an.current=void 0}},[]),m.useEffect(()=>(oo(),()=>oo()),[T,v?.roomId,$t(v?.game),v?.takePreview?.previewId]),m.useEffect(()=>{Ze.current=v;const a=xa.current;if(!a)return;if(md(v)?.previewId===a.previewId){window.clearTimeout(a.timer),xa.current=void 0,sb(!0);return}const s=vm(v);(s?.previewId!==a.previewId||s.turnKey!==a.turnKey)&&(window.clearTimeout(a.timer),xa.current=void 0,oo())},[T,v?.roomId,$t(v?.game),v?.takePreview?.previewId,v?.takePreviewAvailable?.previewId]),m.useEffect(()=>{Ze.current=v,Xt.current?.refresh(),v&&!p&&(yD(v),v.recentResults.length&&Wu(a=>Ym([...v.recentResults.map(n=>({...n,viewerPlayerId:v.playerId})),...a])))},[v]),m.useEffect(()=>{const a=lt.current;Er&&a&&v&&Hp(a,v)},[Er,v?.roomId,$t(v?.game),ni?.playerUrl]),m.useEffect(()=>()=>{Xt.current?.cancel()},[T,v?.roomId,$t(v?.game)]),m.useEffect(()=>{v&&Kg(Math.max(v.players.length,v.maxPlayers??Td))},[v?.roomId,v?.maxPlayers,v?.players.length]),m.useEffect(()=>{if(p||T!=="multi"||v)return;let a;const n=()=>{a!==void 0&&window.clearInterval(a);const d=document.visibilityState==="hidden"?12e4:2e4;a=window.setInterval(()=>{Fl(!0)},d)},s=()=>{n(),document.visibilityState==="visible"&&Fl(!0)};return Fl(),n(),document.addEventListener("visibilitychange",s),()=>{a!==void 0&&window.clearInterval(a),document.removeEventListener("visibilitychange",s)}},[Ou,Uu,v,T]),m.useEffect(()=>{Zc([]),Kn("")},[v?.roomCode,v?.playerId]),m.useEffect(()=>{const a=v?.lobbyDeadlineAt;if(T!=="multi"||v?.status!=="lobby"||v.resultDeadlineAt||!a){ls(void 0);return}const n=Date.parse(a),s=n-10*6e4,d=`${v.roomCode}:${a}`;if(!Number.isFinite(n)||Date.now()>=n){ls(void 0);return}const u=()=>{Xg.current.has(d)||ls(d)};if(Date.now()>=s){u();return}ls(void 0);const h=window.setTimeout(u,Math.max(0,s-Date.now()));return()=>window.clearTimeout(h)},[v?.lobbyDeadlineAt,v?.resultDeadlineAt,v?.roomCode,v?.status,T]),Df.current=(a,n)=>{Rp.current=Rp.current.catch(()=>{}).then(()=>Kf(a,n,!0)).catch(s=>{eo.current="",Wr.current="";const d=s instanceof Error?s.message:"Room turn recovery failed.";G(`Could not restore the current room turn: ${d}`),P(`Could not restore the current room turn: ${d}`)})},m.useEffect(()=>{if(p||T!=="multi"||!v||typeof WebSocket>"u"){cn(!1);return}const a=v;let n=!1,s,d,u,h=0,x=!1,g,S,k,R=0;const C=(J,le)=>{pe?.sessionId&&fd(J,pe.sessionId,{mode:Ge(a.game),roomId:a.roomId,roomStatus:a.status,source:"client",isHost:a.hostPlayerId===a.playerId,browser:navigator.userAgent.
156slice(0,120),platform:navigator.platform.slice(0,120),...le})},U=()=>{s&&(window.clearTimeout(s),s=void 0),d&&(window.clearTimeout(d),d=void 0),u&&(window.clearInterval(u),u=void 0)},A=()=>{if(n||s)return;const J=Math.min(1e4,1500*2**Math.min(h,3));h+=1,s=window.setTimeout(()=>{s=void 0,Q()},J)},F=(J,le,de={})=>{if(n||J!==lt.current)return;const ie=Date.now();g??=ie,C("multiplayer_connection_interrupted",{reason:le,attempt:h+1,connectionDurationMs:Math.max(0,ie-$.get(J)),...de}),d&&(window.clearTimeout(d),d=void 0),u&&(window.clearInterval(u),u=void 0),lt.current=void 0,cn(!1),x=!0,k??=S;const Pe=++R;Qc("connection"),G("Connection interrupted. Trying to reconnect."),k&&MM().then(dt=>{!n&&x&&R===Pe&&rk(k,dt)&&Qc("server-update")});try{J.close(4e3,le)}catch{}A()},$=new WeakMap,Q=()=>{if(n||!yi(a))return;if(!navigator.onLine){A();return}let J;try{J=new WebSocket(CM(a.roomCode))}catch{A();return}lt.current=J,$.set(J,Date.now()),d=window.setTimeout(()=>{d=void 0,F(J,"Connection timeout",{lastServerMessageAgeMs:Date.now()-le})},zA);let le=Date.now();J.addEventListener("open",()=>{n||J!==lt.current||(d&&(window.clearTimeout(d),d=void 0),le=Date.now(),J.send(JSON.stringify({type:"authenticate",playerToken:a.playerToken})),u=window.setInterval(()=>{if(!(J!==lt.current||J.readyState!==WebSocket.OPEN)){if(qA(le)){F(J,"Heartbeat timeout",{lastServerMessageAgeMs:Date.now()-le});return}J.send(FA)}},OA))}),J.addEventListener("message",de=>{if(!(n||J!==lt.current||typeof de.data!="string")){le=Date.now();try{const ie=JSON.parse(de.data);if(ie.type==="room-state"&&ie.room){const Pe=x,dt=rk(k,ie.serverVersion),Je=g===void 0?void 0:Math.max(0,Date.now()-g),Fe=h;x=!1,R+=1,g=void 0,h=0,S=ie.serverVersion||S,k=void 0,cn(!0),Kn(""),Qc(void 0),mi.current=0,Df.current(ie.room,a),Hp(J,ie.room,!0),Pe&&(C("multiplayer_reconnect_succeeded",{attempt:Math.max(1,Fe),reconnectDurationMs:Je}),G("Reconnected to room."),dt&&se("Game server updated · Room restored."));return}if(ie.type==="take-preview-requested"&&ie.previewId&&ie.turnKey){const Pe=Xt.current?.announcement();Pe?.id===ie.previewId&&Pe.value.turnKey===ie.turnKey&&Pe.value.room.roomId===a.roomId&&Xt.current?.request(ie.previewId);return}if(ie.type==="chat-history"&&Array.isArray(ie.messages)){Zc(ie.messages.slice(-200));return}if(ie.type==="chat-message"&&ie.message?.id){Zc(Pe=>Pe.some(dt=>dt.id===ie.message.id)?Pe:[...Pe,ie.message].slice(-200)),Kn("");return}if(ie.type==="chat-error"&&ie.error){Kn(ie.error);return}ie.type==="room-error"&&ie.error&&(x&&C("multiplayer_reconnect_failed",{reason:ie.error,attempt:Math.max(1,h),reconnectDurationMs:g===void 0?void 0:Math.max(0,Date.now()-g)}),Gr(ie.error)?$r(Ea(ie.error)):G(ie.error))}catch{}}}),J.addEventListener("close",de=>{F(J,de.reason||"Connection closed",{closeCode:de.code,wasClean:de.wasClean,lastServerMessageAgeMs:Date.now()-le})}),J.addEventListener("error",()=>{F(J,"Connection error",{lastServerMessageAgeMs:Date.now()-le})})};return cn(!1),Q(),()=>{n=!0,R+=1,U(),lt.current&&(lt.current.close(1e3,"Room changed"),lt.current=void 0),cn(!1),Qc(void 0)}},[v?.playerId,v?.playerToken,v?.roomCode,pe?.sessionId,T]),m.useEffect(()=>{if(p||T!=="multi"||!v||Er){xp.current=0;return}let a=!1,n;const s=()=>{const d=Ze.current;if(a||!d)return;const u=WA(xp.current,d.status,Da(d.game));n=window.setTimeout(async()=>{n=void 0;const h=Ze.current;a||!h||(await bi(h,!0),xp.current+=1,s())},u)};return s(),()=>{a=!0,n&&window.clearTimeout(n)}},[Er,v?.roomId,T]),m.useEffect(()=>{const a=Rn();ds&&a.some(n=>n.id===ds)||Gu(a[0]?.id)},[ds,Le,pt,da,Yt]),m.useEffect(()=>{if(p||T!=="multi"||!v||v.status!=="lobby"||v.recovery||v.roleSelectionActive||v.hostPlayerId!==v.playerId)return;const a=Fp();if(!a)return;const n=`${lu(a)}:${a.roleClaimEnabled}:${a.characters?.join("|")??""}`;El.current!==`${v.roomCode}:${n}`&&(El.current=`${v.roomCode}:${n}`,AR(a))},[ye,H,ds,v,Tr,Le,pt,mt,ba,da,T,Yt]),m.useEffect(()=>{window.scrollTo({top:0,left:0})},[T]),m.useEffect(()=>{if(T!=="workshop")return;let a=!1;return Uc(!0),Re(""),Promise.all([Oy(),Uy()]).then(([n,s])=>{a||(Pr(n),en(s),Re(n.length||s.length?"":"No workshop mods uploaded yet."))}).catch(n=>{a||Re(n instanceof Error?n.message:"Workshop unavailable.")}).finally(()=>{a||Uc(!1)}),()=>{a=!0}},[T]),m.useEffect(()=>{if(!ee)return;const a=()=>
156{if(document.visibilityState==="hidden")return;const n=Date.now();n-Rs.current<lD||n-pp.current<800||(pp.current=n,Ru(s=>s+1))};return window.addEventListener("focus",a),document.addEventListener("visibilitychange",a),()=>{window.removeEventListener("focus",a),document.removeEventListener("visibilitychange",a)}},[ee]),m.useEffect(()=>{if(!ee){ii.current="",Rs.current=0,gl.current=Bc,Do([]),Cr([]),xu(!1);return}if(!Gn||!Wc)return;const a=Bc>gl.current,n=Mr&&a;if(ii.current===ee.id&&!a){const u=Date.now()-Rs.current<cD;if(T!=="mods"||u)return}let d=!1;return pg(!0),km({force:n}).then(async({packs:u,judgeMods:h})=>{if(d)return;Do(u),Cr(h),ii.current=ee.id,Rs.current=Date.now(),gl.current=Math.max(gl.current,Bc);const x=Le.filter(R=>cm(R)&&!u.some(C=>Fd(C,R))),g=pa.filter(R=>!!(R.workshopId||R.workshopSlug)&&!h.some(C=>Zm(C,R)));for(const R of x){if(d)return;await $b(R)}for(const R of g){if(d)return;await od(R)}const S=u.map(R=>({pack:R,installed:$P(R)})).filter(({pack:R,installed:C})=>!C||YD(R,C)),k=h.map(R=>({judgeMod:R,installed:Zb(R)})).filter(({judgeMod:R,installed:C})=>!C||KD(R,C)||n&&hj(C.warnings));if(S.length||k.length){const R=S.length+k.length;Re(`Syncing ${R} subscribed mod${R===1?"":"s"}.`)}for(const{pack:R,installed:C}of S){if(d)return;await Ci(R,{force:!!C,activate:!1})}for(const{judgeMod:R,installed:C}of k){if(d)return;await td(R,{force:!!C,activate:!1})}n&&!S.length&&!k.length&&Re("Mods are up to date.")}).catch(u=>{d||Re(u instanceof Error?u.message:"Workshop subscriptions unavailable.")}).finally(()=>{d||(pg(!1),n&&xu(!1))}),()=>{d=!0}},[ee,Wc,Mr,Gn,T,Bc]),m.useEffect(()=>{const a=new URLSearchParams(window.location.search),n=a.get("install"),s=a.get("slug")?.trim();if(n!=="dub"&&n!=="judge"||!s)return;const d=`${n}:${s}`;if(!(mp.current===d||Ve||!Gn||!Wc)){if(!ee){mp.current=d,ft();return}mp.current=d,window.history.replaceState({},"","/"),K("/"),Y("mods"),on(n),Re("Preparing your subscribed mod..."),(async()=>{try{const{packs:u,judgeMods:h}=await km();if(Do(u),Cr(h),n==="judge"){const g=h.find(S=>S.slug===s);if(!g)throw new Error("Subscribed Judge Mod could not be found. Open Mods and try again.");await td(g);return}const x=u.find(g=>g.slug===s);if(!x)throw new Error("Subscribed Dub Mod could not be found. Open Mods and try again.");await Ci(x)}catch(u){const h=u instanceof Error?u.message:"Automatic install failed. Open Mods and try again.";Re(h),P(h)}})()}},[Ve,ee,Wc,Gn]),m.useEffect(()=>{const a=ro();if(!ee||!a)return;const n=Di(a);if(!n)return;const s=Li(n);if(um(n)||Iu.current.has(s))return;Iu.current.add(s),Sg(u=>({...u,[s]:!0}));let d=!1;return zd(n).then(u=>{d||!u||Hl(u)}).catch(()=>{Iu.current.delete(s)}).finally(()=>{d||Sg(u=>({...u,[s]:!1}))}),()=>{d=!0}},[ee,ys,H,ge,v,pt,Yt]),m.useEffect(()=>{T!=="settings"&&wt&&Lp()},[wt,T]),m.useEffect(()=>{Sn()},[q?.data.roundId,q?.referenceVideoUrl,q?.videoStartSeconds]),m.useEffect(()=>{if(p||!(T==="single"||T==="multi"&&!!v?.game)||!tt||ge||!q)return;const n=OR(q);
156if(UR()){Al.current=n,ot(0),vo(!0);return}if(Al.current===n){oP()||vo(!0);return}Al.current=n;const s=q,d=window.setTimeout(()=>{if(ke.current?.data.roundId!==s.data.roundId)return;if(s.referenceAudioUrl){ki(s);return}if(!fe.current||!s.referenceVideoUrl){vo(!0);return}const h=++xo.current;Tb(s,h).then(x=>{if(Qp(s,h)){if(!x){vo(!0);return}gb(Ql(),h)}})},650);return()=>window.clearTimeout(d)},[q,tt,ge,v?.game,T]),m.useEffect(()=>{if(p||!ge)return;const a=ro();a&&!Bt()&&Ia(a)},[ys,H,ge,v]),m.useEffect(()=>{if(p){dn(!0);return}if(T!=="single"&&T!=="multi"||!ge||Rt||yo){dn(!1);return}dn(!1);const a=window.setTimeout(()=>dn(!0),3500);return()=>window.clearTimeout(a)},[Rt,ge,yo,T]),m.useEffect(()=>{if(p||T!=="single"&&T!=="multi"||!ge||Rt||yo)return;const a=ro();!a||Cl.current===a.id||(Cl.current=a.id,Bt()||Ia(a),window.setTimeout(()=>{T!=="single"&&T!=="multi"||!ge||Rt||(Br.current=!0,_l(0).finally(()=>{window.setTimeout(()=>{Br.current=!1},0)}))},250))},[ys,Rt,H,ge,yo,T]),m.useEffect(()=>{const a=()=>{p||document.visibilityState!=="visible"||(M(),hb(),kR())};return document.addEventListener("visibilitychange",a),window.addEventListener("focus",a),window.addEventListener("pageshow",a),window.addEventListener("online",a),()=>{document.removeEventListener("visibilitychange",a),window.removeEventListener("focus",a),window.removeEventListener("pageshow",a),window.removeEventListener("online",a)}},[q,ae,or,hs,M]),m.useEffect(()=>{const a=()=>{if(document.hidden){if((T==="single"||T==="multi")&&ge&&!Rt&&!yo){const s=fe.current;s&&(Nt||lr.current||!s.paused)&&(Ml.current=!0,bp.current=Number.isFinite(s.currentTime)?s.currentTime:0,dr.current=!1,s.pause(),Ri(),lr.current=!1,Me(!1))}let n=!1;["dub","shorts"].forEach(s=>{const d=Ue.current[s];!d||d.signal.aborted||(Fr.current[s]=!0,d.abort(),n=!0,s==="dub"?wo.current="":zt.current="")}),n&&(Uo.current=!0),wn();return}Qt.current&&window.clearTimeout(Qt.current),Qt.current=window.setTimeout(()=>{if(Qt.current=void 0,Ml.current&&(T==="single"||T==="multi")&&ge&&!Rt&&!yo){const n=bp.current;Ml.current=!1,Kp(),Br.current=!0,_l(n).finally(()=>{window.setTimeout(()=>{Br.current=!1},0)});return}Kp()},300)};return document.addEventListener("visibilitychange",a),()=>document.removeEventListener("visibilitychange",a)},[Rt,ge,Nt,yo,T]);async function hi(){if(pe&&new Date(pe.expiresAt).getTime()>Date.now())return pe;const a=await kM();return Ie(a),a}function Mp(){const a=Ht.current;if(a&&new Date(a.expiresAt).getTime()>Date.now())return a;const n=WE();return Ht.current=n,n}function gi(a){Wx(a),Y(a)}function Wx(a){const n=An(window.location.pathname),s=$N(a);if(s===n){K(s);return}window.history.pushState({},"",s),K(s)}function fi(a){const n=An(a);An(window.location.pathname)!==n&&window.history.pushState({},"",n),K(n),Y("guide")}function Ot(a){if(a==="workshop"){vt?window.location.assign("/workshop"):window.open("/workshop","_blank","noopener,noreferrer");return}if(a==="single"){Lf();return}gi(a)}async function $x(){if(We)try{await We.prompt(),await We.userChoice}finally{Ae(void 0)}}function ft(){const a=`${window.location.pathname}${window.location.search}`||"/";window.location.assign(`/login?return_to=${encodeURIComponent(a)}&lang=${encodeURIComponent(e)}`)}async function ur(){try{await fM(),Jn(null)}catch{P("Could not sign out. Try again.")}}function Lf(){He.current?.cancel(),Ro(),P(Va()||Rn().length?"Choose a dub mod.":"Add a dub mod before playing."),gi("single")}function Ta(){ei.current&&(ei.current.currentTime=0,ei.current.play().catch(()=>{}))}function Gx(){ti.current&&(ti.current.currentTime=0,ti.current.play().catch(()=>{}))}async function qx(){const a=op.current;a&&a.state!=="running"&&a.state!=="closed"&&await a.resume()}function jp(){const a={type:"background-music-claim",ownerId:np.current,activatedAt:Date.now()};ip.current=a,sp.current=!0,vf(!0),ap.current?.postMessage(a)}function Ns(){return vp.current||(vp.current=Promise.allSettled(hS.map(a=>Zk(a))).then(()=>{})),vp.current}function Tp(a){const n=Il.current.get(a.audioUrl);if(n)return n;const s=RD(a).then(d=>{const u=qy(d.samples,d.sampleRate,0);return{referenceAudio:d,referenceFeatures:Sm(u.samples,u.sampleRate)}}).catch(d=>{throw Il.current.delete(a.audioUrl),d});return Il.current.set(a.audioUrl,s),s}function Ep(a){a&&(Tp(a).catch(()=>{}),a.imageUrl&&Zk(a.imageUrl).catch(()=>{}),a.videoUrl&&!kp.current.has(a.videoUrl)&&(kp.current.add(a.videoUrl),QD(a.videoUrl).cat
156ch(()=>{})))}function Hx(a){const n=H?.clips??[],s=La(n.length),d=s.indexOf(a),u=d>=0?s[d+1]:a+1;Ep(u===void 0?void 0:n[u])}function Vf(a){const n=performance.now();if(n-hp.current<32)return;if(hp.current=n,!sr.current){ce(a);return}const s=Math.max(.5,Af.current),d=Math.max(0,(n-Ef.current)/1e3),u=kl.current;if(d<u)return;const h=jh(d,s),x=Math.max(0,Math.min(1,Math.max(a.peak*3.5,a.rms*8))),g=[...vl.current,{progress:h,value:x}];vl.current=g.slice(-360)}function Vl(){vl.current=[],wf([])}m.useEffect(()=>{const a=new Audio("/button_hover.wav");a.volume=$e*Dr,a.preload="auto",ei.current=a;const n=new Audio("/game_button_hover.wav");n.volume=$e*Dr,n.preload="auto",ti.current=n;const s=new Audio("/home-music.mp3"),d=uh($e,Lr);s.volume=d,s.muted=d===0,s.loop=!0,s.preload="metadata",wa.current=s;const u=globalThis.AudioContext??globalThis.webkitAudioContext;let h;if(u)try{h=new u;const S=h.createMediaElementSource(s),k=h.createGain();k.gain.value=d,S.connect(k),k.connect(h.destination),s.volume=1,op.current=h,rp.current=k}catch{h?.close().catch(()=>{}),h=void 0}const x=async()=>{bs.current&&(await h?.resume().catch(()=>{}),await s.play().catch(()=>{})),window.removeEventListener("pointerdown",x),window.removeEventListener("keydown",x)};window.addEventListener("pointerdown",x,{passive:!0}),window.addEventListener("keydown",x);const g=()=>{bs.current&&(s.preload="auto",s.load(),s.play().catch(()=>{}))};return document.documentElement.classList.contains("app-ready")?g():window.addEventListener("voxalike:app-ready",g,{once:!0}),Ns(),()=>{window.removeEventListener("pointerdown",x),window.removeEventListener("keydown",x),window.removeEventListener("voxalike:app-ready",g),a.pause(),a.src="",n.pause(),n.src="",s.pause(),s.src="",rp.current=null,op.current=null,h?.close().catch(()=>{})}},[]),m.useEffect(()=>{if(T!=="single"&&T!=="multi")return;const a=n=>{const s=n.target instanceof Element?n.target.closest("button"):null;!s||s.hasAttribute("disabled")||n.relatedTarget instanceof Node&&s.contains(n.relatedTarget)||Gx()};return document.addEventListener("pointerover",a),()=>document.removeEventListener("pointerover",a)},[T]);function zf(){He.current?.cancel(),Ro(),P("Choose a voice line.")}async function Jx(){if(sn){Ro(),Xc(void 0);return}if(v){await Wl();return}if(q||ge||tt){zf();return}Y("menu")}async function _x(a){const n=Bp(a.game),s=(H&&_k(H,a.game)?H:void 0)??(n?await hr(n):void 0);if(!s){G("Install the Dub Mod used by this result before opening it."),se("Dub Mod required");return}const d=xI(await Od().catch(()=>[]),a),u={...v??{roomId:"history",roomCode:"HISTORY",status:"lobby",hostPlayerId:"",playerId:a.viewerPlayerId??d?.playerId??pc()?.playerId??"history-player",playerToken:"",players:[],createdAt:a.completedAt,expiresAt:a.expiresAt,recentResults:ss},playerId:a.viewerPlayerId??d?.playerId??v?.playerId??"history-player",game:a.game};Ro();let h=!1;if(d)try{const g=await Ip(d),S=RI(a,u.playerId);if(h=S.length>0&&S.every(k=>!!g[k]?.playerAudio),Object.keys(g).length){const k={...je.current,...g};je.current=k,Vo(k)}}catch{}if(Date.parse(a.expiresAt)<=Date.now()&&!h){G("This recent dub expired before a local recording copy was saved."),se("Local recording unavailable");return}Jl(u,s);const x=Gs(u,s);if(!x){G("This result has no downloadable takes for this player."),se("No downloadable takes");return}gn([x]),Xn(xn(a.game.assignments.map(g=>g.index),s.clips.length)),Xc(a),Lt(!0),Zt(!1),Qn(!0),Nr(!0),Ir(!1),Y("multi"),P("Recent dub restored.")}function Yx(){return ss.length?t.jsxs(t.Fragment,{children:[t.jsxs("button",{className:"ghost-button recent-dubs-trigger",type:"button",onClick:()=>cs(!0),children:[t.jsxs("span",{children:[t.jsx(ah,{size:16})," Recent Dubs"]}),t.jsx("strong",{"aria-label":`${ss.length} recent dubs`,children:ss.length})]}),$u?t.jsx("div",{className:"interrupted-dubs-backdrop",role:"presentation",onClick:()=>cs(!1),children:t.jsxs("section",{className:"panel recent-dubs-panel interrupted-dubs-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"recent-dubs-title",onClick:a=>a.stopPropagation(),children:[t.jsxs("div",{className:"panel-header interrupted-dubs-dialog-header",children:[t.jsxs("div",{children:[t.jsxs("span",{children:[t.jsx(ah,{size:13})," RECENT DUBS"]}),t.jsx("h2",{id:"recent-dubs-title",children:"Past Results"}
156),t.jsx("p",{children:"Reopen up to 5 recent multiplayer dubs saved in this browser for up to 7 days."})]}),t.jsx("button",{className:"icon-button",type:"button",onClick:()=>cs(!1),"aria-label":"Close recent dubs",title:"Close",children:t.jsx(Tt,{size:18})})]}),t.jsx("div",{className:"panel-body recent-dubs-list",children:ss.map(a=>t.jsxs("article",{className:"recent-dub-card",children:[t.jsxs("header",{children:[t.jsx("h3",{children:io(a.game.packTitle)}),t.jsx("time",{dateTime:a.completedAt,children:new Date(a.completedAt).toLocaleTimeString([],{hour:"2-digit",minute:"2-digit"})})]}),t.jsxs("p",{children:[Ge(a.game)==="pvp"?"PVP":"Co-op"," · ",a.game.takes.length," recorded takes"]}),t.jsxs("footer",{children:[t.jsx("span",{}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{cs(!1),_x(a)},children:[t.jsx(_e,{size:16}),"View & Download"]})]})]},a.id))})]})}):null]}):null}function Kx(){const a=Yx();return t.jsxs("div",{className:"room-dub-history",children:[t.jsx(z0,{drafts:Ux,onImport:fR,onDelete:n=>{Te.current?.id===n&&(Te.current=void 0),kf(s=>s.filter(d=>d.id!==n))}}),a]})}async function Of(a,n,s,d,u,h){const x=u??Le.find(k=>k.id===ye);if(!x)return;const g=h?.game?kn(h):void 0,S=a==="multi"&&h?`multi:${h.roomCode}:${g??"lobby"}:${h.playerId}`:`single:${x.id}:${Date.now()}`;try{const k=await S0({id:S,mode:a,status:"active",sourceId:x.id,packId:n.id,packTitle:n.title,currentClipIndex:d,clipSequence:s,roomCode:h?.roomCode,roomRunKey:g,playerId:h?.playerId,roomGame:h?.game});if(Te.current=k,a==="single"){sl(void 0);const R=await Od().catch(()=>[]);await Promise.all(R.filter(C=>w0(C,k.id)).map(C=>Sc(C.id).catch(()=>{})))}return k}catch{P("Game started, but local recovery storage is unavailable.");return}}async function Ap(a,n){const s=Te.current;if(!s||!n.playerAudio)return!1;try{return await sv(s.id,{takeKey:a,clipId:n.clipId,audioBlob:En(n.playerAudio),playerFeatures:n.playerFeatures,score:n.score,keptOriginal:n.keptOriginal}),!0}catch{return!1}}function Uf(a){const n=Te.current,s=a.game;if(!n||n.mode!=="multi"||!s||n.roomCode!==a.roomCode)return;const d=`${n.id}:${s.currentAssignmentIndex}:${s.takes.map(u=>`${u.assignmentIndex}:${u.audioDataUrl}`).join("|")}`;d!==jf.current&&(up.current=up.current.catch(()=>{}).then(async()=>{const u=Te.current;if(!u||u.id!==n.id)return;const h=await ac(n.id,{roomGame:s,currentClipIndex:to(a)?.index??n.currentClipIndex,status:s.completedAt?"complete":"active"});h&&(Te.current=h);const x=await fc(n.id),g=new Map(x.map(S=>[S.takeKey,S]));for(const S of s.takes){const k=$s(S.assignmentIndex,S.clipId),R=g.get(k);let C=R?.audioBlob;if(!C){const U=await fetch(S.audioDataUrl,{credentials:"same-origin",cache:"no-store"});if(!U.ok)throw new Error("Room recovery audio is temporarily unavailable.");C=await U.blob()}if(!C.size||C.size>3e6)throw new Error("Room recovery audio is too large.");(!R||R.assignmentIndex!==S.assignmentIndex||R.playerId!==S.playerId||R.playerName!==S.playerName)&&await sv(n.id,{takeKey:k,clipId:S.clipId,audioBlob:C,playerFeatures:R?.playerFeatures,score:S.score??R?.score,keptOriginal:R?.keptOriginal,assignmentIndex:S.assignmentIndex,playerId:S.playerId,playerName:S.playerName})}jf.current=d}))}async function Ip(a){const n=await fc(a.id),s={};for(const d of n){const u=await Dk(d.audioBlob);s[d.takeKey]={clipId:d.clipId,playerUrl:URL.createObjectURL(d.audioBlob),playerAudio:u,playerFeatures:d.keptOriginal?void 0:d.playerFeatures??Sm(u.samples,u.sampleRate),score:d.score,keptOriginal:d.keptOriginal}}return s}async function Zx(){const a=oi;if(!(!a||vs)){cl(!0);try{await Sc(a.id),Te.current?.id===a.id&&(Te.current=void 0),sl(void 0),P("Saved draft discarded.")}catch{P("Saved draft could not be removed.")}finally{cl(!1)}}}async function Ff(a){const n=Te.current;!n||n.mode!==a||(Te.current=void 0,await Sc(n.id).catch(()=>{}))}async function Xx(){const a=oi;if(!a||vs)return;const n=Le.find(s=>s.id===a.sourceId);if(!n){P("The Dub Mod used by this draft is no longer installed.");return}cl(!0),ka(!0),P("Restoring saved dub.");try{const s=ye===n.id&&H?H:await hr(n);if(!s)return;const d=await Ip(a);Ro(),je.current=d,Vo(d),Te.current=a,sl(void 0);const u=xn(a.clipSequence,s.clips.length),h=u[0]??0,x=u.includes(a.currentClipIndex)?a.currentClipIndex:h;if(Xn(u),va(x),Y("single"),a.status==="complete"){const k=a.finalScore??Nd(d,s.clips),R={id:`${a.id}:restored`,packId:s.id,packTitle:s.title,createdAt:a.updatedAt,clips:u.map(C=>s.clips[C]).filter(C=>!!C),takes:Object.fromEntries(Object.entries(d).map(([C,U])=>[C,{clipId:U.clipId,playerAudio:U.playerAudio,playerUrl:U.playerUrl,score:U.score}])),score:k,completedCount:Object.keys(d).length,backingTrackUrl:s.backingTrackUrl};gn([R]),Zt(!1),Lt(!0),Qn(!1),Nr(!1),Bt()||Ia(R),P("Completed dub restored.");return}const g=s.clips[x];if(!g)throw new Error("The saved clip is no longer available in this mod.");const S=Mp();await Bs(g,x,!0,g.id,S,"local"),Zt(!0),Lt(!1),P(`Draft restored at clip ${u.indexOf(x)+1}.`)}catch(s){P(s instanceof Error?`Draft restore failed: ${s.message}`:"Draft restore failed.")}finally{ka(!1),cl(!1)}}async function Bf(a,n=0,s,d="single"){const u=d==="single";if(Ti(a)){P("This mod is being removed. Choose another mod."),Y(d);return}u&&(ka(!0),xe(void 0),P("Loading mod."));try{const h=ye===a.id&&H?H:await hr(a);if(!h){u&&xe(Xk("mod-load",Jc.current,a));return}await Np(n,h,s,d,a)}finally{u&&ka(!1)}}async function Np(a=0,n=H,s,d="single",u){const h=d==="single",x=Le.find(U=>U.id===ye);if(!u&&x&&Ti(x)){P("This mod is being removed. Choose another mod."),Y(d);return}if(h&&(ka(!0),xe(void 0)),!n){P("Add a dub mod before playing."),Y(d),h&&ka(!1);return}const g=n.clips,S=xn(s,g.length),k=S.includes(a)?a:S[0],R=k===void 0?void 0:g[k];if(k===void 0||!R){P("Add a dub mod before playing."),Y(d),h&&ka(!1);return}let C="session";try{Ro(),P("Preparing stage.");const U=d==="single"?Mp():await hi();Xn(S),Lt(!1),va(k),C="recovery-save",await Of("single",n,S,k,u),C="clip-audio",await Bs(R,k,!0,R.id,U,d==="single"?"local":"server"),C="stage-open",oR(U,n,S,u),Zt(!0),Y(d)}catch(U){Zt(!1),te(void 0);const A=Xk(C,U,u,n,R);xe(A),P(A.message)}finally{h&&ka(!1)}}async function Qx(){const a=H?.clips??[],n=La(a.length),s=n.indexOf(xt),d=s>=0?n[s+1]:xt+1,u=d===void 0?void 0:a[d];if(d===void 0||!u){Vi(),Lt(!0),Zt(!1),Ir(!1),te(void 0),Cn(),P("Run complete."),Vt(!1);return}if(Vt(!0),Te.current){const h=await ac(Te.current.id,{currentClipIndex:d}).catch(()=>{});h&&(Te.current=h)}P(`Loading clip ${d+1}.`),Ep(u),va(d);try{await Bs(u,d,!0,u.id,void 0,"local")}catch(h){Vt(!1),P(h instanceof Error?`Next clip failed: ${h.message}`:"Next clip failed.")}}function Dp(){xl.current&&(URL.revokeObjectURL(xl.current),xl.current=void 0)}function zl(){Dp(),Se(void 0)}function Wf(a,n){const s=URL.createObjectURL(n);return Dp(),xl.current=s,Se({workId:a,url:s}),s}function eR(a,n){if(ro()?.id!==a.id||lr.current)return;const s=fe.current;s&&!s.paused&&!s.ended||Wf(a.id,n)}function Ro(){Vi(),Ri(),zl(),Me(!1),Sb(),wn(),rt.current={},Fr.current={},Uo.current=!1,Ml.current=!1,bp.current=0,Qt.current&&(window.clearTimeout(Qt.current),Qt.current=void 0),lr.current=!1,dr.current=!1,qr(),_r(!0),Ob(je.current),je.current={},Tl.current=void 0,ja.current=!1,Vo({}),va(0),Xn([]),Zt(!1),Lt(!1),Ir(!1),Qn(!1),dn(!1),Nr(!1),vo(!1),Vt(!1),gs(!1),un(void 0),tp(!1),al(!1),Zu(!1),ot(0),Sa(!1),nr(!1),Vl(),yp.current="",Al.current="",It(!1),te(void 0),Cn(),la(void 0),wo.current="",zt.current="",Cl.current="",Ur.current={}}function $f(){Ro(),Ma.current="",eo.current="",Wr.current="",So.current="",ja.current=!1,pi.current=void 0}function tR(a){if(!H)return;const n=Fb(),s={...je.current},d=Ut();a&&d&&s[d.id]&&!s[d.id].keptOriginal&&(s[d.id]={...s[d.id],score:a});const u=n.reduce((k,R)=>{const C=s[R.id];return C?.playerAudio&&(k[R.id]={clipId:R.id,playerAudio:Lk(C.playerAudio),score:C.score,keptOriginal:C.keptOriginal}),k},{}),h=Object.keys(u).length;
156if(!h)return;const x=Le.find(k=>k.id===ye),g=Os(H,x),S={id:`${H.id}-${Date.now()}`,packId:H.id,packTitle:H.title,workshopPackId:g?.id,workshopPackSlug:g?.slug,createdAt:Date.now(),clips:n,takes:u,score:Nd(s,n)??a,completedCount:h,backingTrackUrl:H.backingTrackUrl};return zl(),bn.current={},Sl.current={},Ca.current={},Ps.current={},ci.current={},Rl.current={},zr.current={},li.current={},wo.current="",zt.current="",gn(k=>[S,...k.filter(R=>R.packId!==S.packId)]),S}function oR(a,n,s,d){const u=uL(),h=aR(n,d,{runId:u,clipCount:s.length,startedAt:new Date().toISOString(),source:"client"});Tl.current={runId:u,sessionId:a.sessionId,startedAt:Date.now(),payload:h},fd("single_run_started",a.sessionId,h)}function rR(a,n){const s=Tl.current;if(!s)return;const d={...s.payload,completedAt:new Date().toISOString(),completedCount:n?.completedCount??Object.keys(je.current).length,scoreTotal:n?.score?.total??a?.total,scoringVersion:a||n?.score?PE:void 0,durationScore:n?.score?.duration??a?.duration,timingScore:n?.score?.timing??a?.timing,energyScore:n?.score?.energy??a?.energy,pitchScore:n?.score?.pitch??a?.pitch,durationMs:Date.now()-s.startedAt};Tl.current=void 0,fd("single_run_completed",s.sessionId,d)}function aR(a,n,s){const d=n??Le.find(h=>h.id===ye),u=Os(a,d);return{mode:"single",packId:a.id,packTitle:a.title,packSource:u?"workshop":"local",workshopPackId:u?.id,workshopPackSlug:u?.slug,...s}}async function Gf(){if(!yt()&&qj(b.stream))return b.stream;P(b.stream?"Reconnecting microphone.":"Requesting microphone.");const a=await y(b.selectedDeviceId,Ee);return lp.current=a?.errorMessage,Kd(a?.stream)?a.stream:void 0}function qf(a){ws.current=a,a==="media-recorder"&&P("Starting compatible mobile recorder...")}async function Ol(a,n,s,d){const u=await Qk(a,n,s,d);oa(u),P(u.message)}async function Ul(a,n,s){if(si.current)return;si.current=a,_r(!1),Ls();const d=He.current;He.current=void 0,d?.cancel(),cr.current=!1,fe.current?.pause(),It(!1),ra(!1),s==="mic-check"&&po("idle"),ce({rms:0,peak:0}),uo(s==="recording");const u=await Qk(s,new Error(a.message),n,a);E(a.code==="digital-silence"?"The browser stopped sending microphone audio. Reconnect the microphone before recording again.":"The microphone was interrupted. Reconnect it before recording again."),oa(u),To({...u,source:s}),P(u.message)}function nR(){const a=vr?.source;si.current=void 0,To(void 0),oa(void 0),uo(!1),a==="mic-check"?Ds():Gp()}async function Ds(){if(ri.current)return;wa.current?.pause();const a=rv();He.current?.cancel(),Ls(),Vs(),mo(void 0),ri.current=!0,ws.current="permission",si.current=void 0,To(void 0),oa(void 0),lo(!0),P("Connecting microphone...");try{const n=await Gf();if(!n){a?.close(),await Ol("permission",lp.current),po("idle");return}const s=new av(n,Vf,{audioContext:a,preferMediaRecorder:yt(),onStartupStage:qf,onRuntimeFailure:u=>{Ul(u,n,"mic-check")}});He.current=s,ce({rms:0,peak:0});const d=!Ni(Ee);po(d?"voice":"noise"),ra(!0),P(d?"Mic check: speak at your normal volume.":"Mic check: stay silent for 2 seconds.");try{await s.start()}catch(u){s.cancel(),He.current=void 0,ra(!1),po("idle"),await Ol(ws.current,u,n);return}if(d){ai.current=window.setTimeout(()=>{Hf()},_m);return}ai.current=window.setTimeout(()=>{po("voice"),P("Mic check: speak at your normal volume."),ai.current=window.setTimeout(()=>{Hf()},_m)},Mk)}finally{ri.current=!1,lo(!1)}}async function Hf(){const a=He.current;if(a){Ls();try{const n=await a.stop(),s=iR(n);mo(s),po("ready"),P(Ni(Ee)?"Mic check ready. Apply it to use this cleanup in-game.":"Mic check ready. Listen to the browser-processed sample.")}catch(n){po("idle"),P(n instanceof Error?`Mic check failed: ${n.message}`:"Mic check failed.")}finally{He.current=void 0,ra(!1),ce({rms:0,peak:0})}}}function Lp(){Ls(),He.current?.cancel(),He.current=void 0,ra(!1),ce({rms:0,peak:0}),po(he?"ready":"idle"),P("Mic check stopped.")}function iR(a){const n=Ni(Ee)?Math.min(a.samples.length,Math.round(Mk/1e3*a.sampleRate)):0,s=Math.round(_m/1e3*a.sampleRate),d=n,u=Math.min(a.samples.length,d+s),h=a.samples.slice(d,u),x=h.length?h:a.samples,g={samples:new Float32Array(x),sampleRate:a.sampleRate},S=n>0?bj(a.samples.subarray(0,n),a.sampleRate):0,k=Ni(Ee)?Hy(g,S,$y(Ee)):{samples:new Float32Array(g.samples),sampleRate:g.sampleRate},R=Tn(gc(g,mn)),C=Tn(gc(k,mn));dl.current={raw:R,cleaned:C};const U=so(g.samples),A=br(g.samples);return{rawUrl:R,cleanedUrl:C,rawAudio:g,cleanedAudio:k,noiseFloorRms:S,applied:!1,noiseLabel:rL(S),volumeLabel:aL(U),peakLabel:nL(A),summary:iL(S,U,A)}}function Ls(){ai.current&&(window.clearTimeout(ai.current),ai.current=void 0)}function Vs(){ul.current?.pause(),ul.current=null;const a=dl.current;a.raw&&URL.revokeObjectURL(a.raw),a.cleaned&&URL.revokeObjectURL(a.cleaned),dl.current={}}function Vp(a){const n=a==="raw"?he?.rawUrl:he?.cleanedUrl;if(!n){P("Run mic check first.");return}ul.current?.pause();const s=new Audio(n);s.volume=Math.max(0,Math.min(1,$e)),ul.current=s,s.play().catch(()=>{P("Mic check playback failed.")})}
156function sR(){if(!he){P("Run mic check first.");return}aa(he.noiseFloorRms),Fn(!0),mo({...he,applied:!0});try{localStorage.setItem(sg,String(he.noiseFloorRms)),localStorage.setItem(cg,"1"),P("Mic check applied to future recordings.")}catch{P("Mic check applied for this session. Browser storage is unavailable, so it may need to be applied again later.")}}async function cR(a){wt&&Lp(),Vs(),mo(void 0),po("idle"),aa(0),Fn(!1),ct(Ek(a)),Ak(),await w(a,Ee)}function lR(a){const n=fu(a);ct(n),bD(b.selectedDeviceId,n)}function dR(a){const n=gu(a);if(Vx(n),!he)return;Vs();const s=Tn(gc(he.rawAudio,n)),d=Tn(gc(he.cleanedAudio,n));dl.current={raw:s,cleaned:d},mo({...he,rawUrl:s,cleanedUrl:d})}async function uR(a){if(!(a===Ee||ae||wt)){Vs(),mo(void 0),aa(0),Fn(!1),Ak(),Ao(a);try{localStorage.setItem(BS,a)}catch{}if(b.stream){lo(!0);const n=await y(b.selectedDeviceId,a);if(lo(!1),!Kd(n.stream)){P(n.errorMessage||"Microphone reconnect failed.");return}}P(a==="raw"?"Raw microphone selected.":a==="natural"?"Natural browser noise reduction selected.":"Clean voice processing selected.")}}function se(a){rf(a),js.current&&window.clearTimeout(js.current),js.current=window.setTimeout(()=>rf(""),2400)}function pR(a){af(a),Ts.current&&window.clearTimeout(Ts.current),Ts.current=window.setTimeout(()=>af(void 0),1600)}async function Jf(a,n,s){try{await navigator.clipboard.writeText(a),pR(n),se(s)}catch{G("Copy failed. Select the code and copy it manually."),se("Copy failed")}}function mR(a=v?.roomCode){const n=new URL(window.location.href);return n.searchParams.set("room",a??Vu),n.toString()}function hR(){v?.roomCode&&Jf(v.roomCode,"code","Room code copied")}function gR(){v?.roomCode&&Jf(mR(v.roomCode),"link","Invite link copied")}async function Fl(a=!1){a||_g(!0);try{const n=await xM({language:Ou,status:Uu});vx(n),Yg("")}catch(n){Yg(n instanceof Error?n.message:"Public lobby unavailable.")}finally{a||_g(!1)}}async function fR(a){if(or||v)throw new Error("Leave the current room before importing a Recovery ZIP.");Lo("create"),G("Checking Recovery ZIP.");let n;try{const s=await C0(a);if(s.kind==="legacy-recordings"){await bR(s);return}if(Yn==="public"&&!ee)throw new Error("Sign in before restoring a public room.");const d={...s.game,takes:[]};if(Yn==="public"&&d.packSource!=="workshop")throw new Error("Public rooms require a Workshop mod. Restore this local mod as a private room.");const u=Bp(d)??await Zf(d);if(!u)throw new Error("Install the exact dub mod used by this Recovery ZIP before importing it.");const h=ye===u.id&&H?H:await hr(u);if(!h)throw new Error("The recovery mod could not be opened. Reinstall the exact mod and try again.");const x=Xf(h,d);if(x)throw new Error(x);const g=await hi();n=await Dy({sessionId:g.sessionId,playerName:is,visibility:Yn,language:zu});const S=[];for(const[R,C]of s.recordings.entries()){G(`Uploading recovered recording ${R+1} of ${s.recordings.length}.`);const U=await TM({roomCode:n.roomCode,playerToken:n.playerToken,assignmentIndex:C.take.assignmentIndex,clipId:C.take.clipId,playerId:C.take.playerId,runKey:d.startedAt,audioDataUrl:await Dd(C.audioBlob)});S.push({...C.take,audioDataUrl:U.audioDataUrl})}const k=await EM({roomCode:n.roomCode,playerToken:n.playerToken,game:{...d,takes:S}});Ma.current="",eo.current="",So.current="",bo(void 0),Gu(u.id),us("coop"),ps("claim"),Ne(k),fa(k.roomCode),G("Recovery loaded. Invite the other players, then start when everyone is ready."),se("Recovery room created")}catch(s){throw n&&await Vy({roomCode:n.roomCode,playerToken:n.playerToken}).catch(()=>{}),s}finally{Lo(void 0)}}async function bR(a){const n=Kk(a.manifest.packTitle),d=Le.filter(U=>[U.summary?.title,U.name].filter(F=>!!F).some(F=>Kk(F)===n)).sort((U,A)=>U.id===ye?-1:A.id===ye?1:U.id===Tr?-1:A.id===Tr?1:A.savedAt-U.savedAt)[0];if(!d)throw new Error(`Install "${a.manifest.packTitle}" first, then import this older Recovery ZIP again.`);const u=ye===d.id&&H?H:await hr(d);if(!u)throw new Error("The recovery mod could not be opened. Reinstall the exact mod and try again.");const h=new Map;for(const U of a.recordings){const A=h.get(U.clipId),F=Date.parse(U.savedAt),$=A?Date.parse(A.savedAt):Number.NEGATIVE_INFINITY;(!A||!Number.isFinite($)||!Number.isFinite(F)||F>=$)&&h.set(U.clipId,U)}const x=new Map(u.clips.map(U=>[U.id,U]));if([...h.keys()].filter(U=>!x.has(U)).length)throw new Error(`The installed "${u.title}" does not match this older Recovery ZIP. Reinstall the exact mod version used for the recording.`);Ro();const S={},k=[];try{let U=0;for(const[A,F]of h){U+=1,G(`Restoring recording ${U} of ${h.size}.`);const $=URL.createObjectURL(F.audioBlob);
156k.push($),S[A]={clipId:A,playerUrl:$,playerAudio:await Dk(F.audioBlob)}}}catch(U){throw k.forEach(A=>URL.revokeObjectURL(A)),new Error(U instanceof Error?`A saved recording could not be decoded: ${U.message}`:"A saved recording could not be decoded.")}je.current=S,Vo(S);const R=Os(u,d),C={id:`legacy-recovery-${u.id}-${Date.now()}`,packId:u.id,packTitle:u.title,workshopPackId:R?.id,workshopPackSlug:R?.slug,createdAt:Date.parse(a.manifest.updatedAt)||Date.now(),clips:u.clips,takes:Object.fromEntries(Object.entries(S).map(([U,A])=>[U,{clipId:U,playerAudio:A.playerAudio,playerUrl:A.playerUrl}])),completedCount:Object.keys(S).length,backingTrackUrl:u.backingTrackUrl};gn([C]),Xn(xn(void 0,u.clips.length)),va(0),Zt(!1),Lt(!0),Qn(!0),Nr(!0),Ir(!1),Y("single"),P(`Recovered ${C.completedCount} saved recordings from the older ZIP.`),G(""),Bt()||Ia(C)}async function yR(){if(!or){if(Yn==="public"){if(!ee){G("Sign in to create a public room."),se("Sign in to create a public room");return}const a=Fp();if(!a||a.packSource!=="workshop"){G("Public rooms require a Workshop mod."),se("Public rooms require a Workshop mod");return}}Lo("create"),G("Creating room.");try{const a=await hi(),n=await Dy({sessionId:a.sessionId,playerName:is,visibility:Yn,language:zu});Ma.current="",eo.current="",So.current="",bo(void 0),Ne(n),fa(n.roomCode),G("Room ready."),se("Room created")}catch(a){const n=a instanceof Error?a.message:"Room creation failed.";G(n),se(n)}finally{Lo(void 0)}}}async function _f(a=Vu){if(!or){Lo("join"),G("Joining room.");try{const n=await hi(),s=await RM({sessionId:n.sessionId,roomCode:a.trim().toUpperCase(),playerName:is});Ma.current="",eo.current="",So.current="",bo(void 0),s.status==="playing"&&s.game&&await pr(s),s.status==="lobby"&&s.lobbyPack?.roleClaimEnabled&&(us("coop"),ps("role")),Ne(s),fa(s.roomCode),G("Joined room."),se("Joined room")}catch(n){const s=n instanceof Error?n.message:"Join failed.";G(s),se(s)}finally{Lo(void 0)}}}function vR(a){fa(a),_f(a)}async function Bl(a,n=1,s=!1){if(!s&&Nl.current||Ze.current||!ok(a.expiresAt))return;const d=++Es.current;Nl.current=!0,Y("multi"),fa(a.roomCode),Lo("join"),G(n>1?"Reconnecting to room.":"Restoring your room.");try{const u=await Ly({roomCode:a.roomCode,playerToken:a.playerToken});if(d!==Es.current)return;mi.current=0,Ma.current="",eo.current="",So.current="",bo(void 0),u.status==="playing"&&u.game&&await pr(u),u.status==="lobby"&&u.lobbyPack?.roleClaimEnabled&&(us("coop"),ps("role")),Ne(u),fa(u.roomCode),G(u.status==="playing"?"Room restored.":"Room restored. Waiting in lobby."),se("Room restored")}catch(u){if(d!==Es.current)return;const h=u instanceof Error?u.message:"Room restore failed.";if(zs(h)&&ok(a.expiresAt)){G("Connection interrupted. Trying to restore room."),Lo(void 0),Ct.current=window.setTimeout(()=>{Ct.current=void 0,Bl(a,n+1)},BA(n));return}Gr(h)?(du(),G("Saved room expired. Enter your name and join a new room.")):(G(h),se(h))}finally{d===Es.current&&(Nl.current=!1,Ct.current||Lo(void 0))}}function kR(){const a=Ze.current;if(a){bi(a,!0);return}if(or&&!Ct.current)return;const n=pc();n&&(Ct.current&&(window.clearTimeout(Ct.current),Ct.current=void 0),Bl(n))}async function zp(){if(!Ar){Ct.current&&(window.clearTimeout(Ct.current),Ct.current=void 0),Lo(void 0),Hu(!0),bo(void 0),So.current="",eo.current="",Wr.current="",al(!1),G("Reconnecting to room.");try{const a=Ze.current;if(a){const s=lt.current;if(s)try{s.close(4e3,"Manual reconnect")}catch{}cn(!1),await bi(a,!1,!0,!0);return}const n=pc();if(!n){G("The saved room session is no longer available. Join the room again with its code.");return}await Bl(n,1,!0)}finally{Hu(!1)}}}async function Yf(){if(!v||ya)return;const a=Gl(v);if(a){G(a),se(a);return}if(!v.recovery&&mt==="coop"&&ba==="role"&&!v.roleSelectionActive){ol(!0);try{const s=vn(),d=Va();if(!(s&&ye===s.id&&H?d:s?await hr(s):d))throw new Error("Choose a local dub mod before starting.");const h=await zM({roomCode:v.roomCode,playerToken:v.playerToken});Ne(h),G("Choose your roles.")}catch(s){const d=s instanceof Error?s.message:"Could not start role selection.";G(d),se(d)}finally{ol(!1)}return}const n=(v.recovery||v.roleSelectionActive)&&v.lobbyPack?Us(v.lobbyPack):vn();ol(!0),G("Starting room.");try{for(const h of[3,2,1])Qu(h),await Gd(380);Qu(void 0);const s=Va(),d=n&&ye===n.id&&H?s:n?await hr(n):s;if(!d)throw new Error("Choose a local dub mod before starting.");if((v.recovery||v.roleSelectionActive)&&v.lobbyPack){const h=$l(d,v.lobbyPack);if(h)throw new Error(h)}const u=await jM(dI(v,()=>DR(d,v)));G(`Room game started: ${io(u.game?.packTitle??d.title)}.`),await pr(u,d),Ne(u)}catch(s){const d=s instanceof Error?s.message:"Room start failed.";G(d),se(d)}finally{ol(!1),Qu(void 0)}}function wR(){return!!(v?.lobbyPack&&(v.roleSelectionActive||v.hostPlayerId!==v.playerId))&&v?.lobbyPack?Us(v.lobbyPack):vn()}function SR(){const a=wR();if(!!(v?.lobbyPack&&(v.roleSelectionActive||v.hostPlayerId!==v.playerId))&&!a){const s=Va();return s&&v?.lobbyPack&&!$l(s,v.lobbyPack)?s:void 0}if(!(a&&a.id!==ye))return Va()}function xR(){const a=SR();if(a)return JD(HD(a.clips))}async function RR(){if(!(!v?.roleSelectionActive||ya))try{const a=await OM({roomCode:v.roomCode,playerToken:v.playerToken});Ne(a),G("Returned to lobby.")}catch(a){const n=a instanceof Error?a.message:"Could not return to lobby.";G(n),se(n)}}async function bi(a=v,n=!1,s=!1,d=!1){if(!a||Dl.current&&!d)return;const u=++As.current;Dl.current=!0;try{const h=await Ly({roomCode:a.roomCode,playerToken:a.playerToken});if(u!==As.current||!yi(a))return;mi.current=0,await Kf(h,a,n,s)}catch(h){if(u!==As.current||!yi(a))return;const x=h instanceof Error?h.message:"Reconnect failed.";if(zs(x)){mi.current+=1,(!n||mi.current>
156=3)&&(G("Connection interrupted. Trying to reconnect."),n||se("Reconnecting"));return}if(Gr(x)){$r(Ea(x)),n||se(vi(x));return}n||(G(x),se(x))}finally{u===As.current&&(Dl.current=!1)}}async function Kf(a,n,s,d=!1){if(!yi(n))return;const u=MR(a),h=Ze.current,x=qE(h,u).filter(R=>Zg.current.has(R.id)?!1:(Zg.current.add(R.id),!0));if(x.length){const R=new Date().toISOString(),C=(A,F)=>A==="disconnected"?r("{name} disconnected.",{name:F}):A==="reconnected"?r("{name} reconnected.",{name:F}):r("{name} left the room.",{name:F}),U=x.map(A=>({id:A.id,kind:"system",text:C(A.kind,A.playerName),createdAt:R}));Zc(A=>[...A,...U.filter(F=>!A.some($=>$.id===F.id))].slice(-200))}const g=h?.game;if(!sn&&u.status==="lobby"&&!u.game&&!!(h&&g&&Da(g)&&ge)&&h&&g){const R=Gs(h,H),C=u.recentResults.find(A=>A.id===g.startedAt)??{id:g.startedAt,game:g,completedAt:g.completedAt??new Date().toISOString(),expiresAt:new Date(Date.now()+7200*1e3).toISOString()};R&&gn(A=>[R,...A.filter(F=>F.id!==R.id)]);const U={...C,viewerPlayerId:h.playerId};Wu(A=>Ym([U,...A])),Xc(U),Ne(u),G("The host returned to the room. Your finished dub is still available here."),P("The host returned to the room. You can finish or retry your download.");return}if(!sn&&u.status==="lobby"&&!u.game&&!!(Ze.current?.game||q||tt||ge))$f();else if(u.status==="playing"&&u.game&&(await pr(u,void 0,d),!yi(n)))return;Ne(u),s||G("Room refreshed.")}async function PR(a){if(a.hostPlayerId===a.playerId)return!0;const n=a.lobbyPack;if(!n){const h="Waiting for host to choose a dub mod.";return G(h),se(h),!1}let s;if((n.packSource==="workshop"||n.workshopPackId||n.workshopPackSlug)&&(s=await tb(n),!await ob(n,s)))return!1;let d=Us(n);if(d){d=await rb(d,n);const h=ye===d.id&&H?H:await hr(d);if(!h){const g="Installed dub mod could not be loaded.";return G(g),se(g),!1}const x=$l(h,n);return x?(G(x),se(x),!1):!0}if(s??=await tb(n),!s){const h=n.packSource==="workshop"?`Workshop mod unavailable: ${io(n.packTitle)}.`:`Host is using a local-only mod: ${io(n.packTitle)}.`;return G(h),se(h),!1}G(`Installing ${io(s.title)}.`),se("Installing room mod"),fg(s.id);let u;try{u=await Ci(s)}finally{fg(h=>h===s.id?void 0:h)}if(u?.pack){const h=$l(u.pack,n);if(h)return G(h),se(h),!1}if(!u?.pack&&!Us(n)){const h="Install failed. Try again before readying up.";return G(h),se(h),!1}return!0}async function CR(a){if(!v||a&&!await PR(v))return;const n=++wp.current;pi.current={requestId:n,ready:a};const s=v,d={...v,players:v.players.map(u=>u.id===v.playerId?{...u,isReady:a,lastSeenAt:new Date().toISOString()}:u)};Ne(d),G(a?"Ready.":"Not ready.");try{const u=await NM({roomCode:v.roomCode,playerToken:v.playerToken,ready:a,lobbyPackKey:a&&v.lobbyPack?lu(v.lobbyPack):void 0});n===wp.current&&(pi.current=void 0,Ne(u),G(a?"Ready.":"Not ready."))}catch(u){if(n!==wp.current)return;pi.current=void 0;const h=u instanceof Error?u.message:"Ready state failed.";if(Gr(h)){$r(Ea(h)),se(vi(h));return}Ne(s),G(h),se(h)}}function MR(a){const n=pi.current;return!n||a.status!=="lobby"?a:{...a,players:a.players.map(s=>s.id===a.playerId?{...s,isReady:n.ready}:s)}}async function Wl(){if(!v)return;const a=v.hostPlayerId===v.playerId;if(!window.confirm(a?"Leave and close this room for everyone?":"Leave this room?"))return;const s=v;G(a?"Closing room.":"Leaving room.");try{const d=await Vy({roomCode:s.roomCode,playerToken:s.playerToken});await Ff("multi"),$r(d.roomClosed?"Room closed.":"Left room."),se(d.roomClosed?"Room closed":"Left room")}catch(d){const u=d instanceof Error?d.message:"Leave room failed.";if(Gr(u)){$r(Ea(u)),se(vi(u));return}G(u),se(u)}}
156async function jR(){if(!(!v?.game||Ku)){if(v.hostPlayerId!==v.playerId){G("Waiting for host to return to room."),se("Waiting for host");return}Zu(!0),G("Returning to room.");try{const a=await AM({roomCode:v.roomCode,playerToken:v.playerToken});await Ff("multi"),$f(),Ne(a),G("Back in room."),se("Back in room")}catch(a){const n=a instanceof Error?a.message:"Return to room failed.";if(Gr(n)){$r(Ea(n)),se(vi(n));return}G(n),se(n)}finally{Zu(!1)}}}async function TR(a,n){if(!(!v||v.hostPlayerId!==v.playerId||!window.confirm(`Kick ${n||"this player"} from this room? They will not be able to rejoin for 5 minutes.`))){G(`Removing ${n||"player"}.`);try{const d=await FM({roomCode:v.roomCode,playerToken:v.playerToken,targetModerationUserId:a});Ne(d),G(`${n||"Player"} removed for 5 minutes.`),se("Player removed for 5 minutes")}catch(d){const u=d instanceof Error?d.message:"Kick failed.";if(Gr(u)){$r(Ea(u)),se(vi(u));return}G(u),se(u)}}}async function ER(a,n,s,d){if(!(!v||v.visibility!=="public"))try{await BM({roomCode:v.roomCode,playerToken:v.playerToken,targetModerationUserId:a,reportReason:s,reportDetails:d}),se(`${n} reported`)}catch(u){const h=u instanceof Error?u.message:"Report failed.";G(h),se(h)}}function Op(a){const n=lt.current;if(!n||n.readyState!==WebSocket.OPEN){Kn("Room chat is reconnecting.");return}Kn(""),n.send(JSON.stringify({type:"chat-send",text:a}))}function $r(a){const n=Ze.current,s=n?.game,d=ge?Gs(n,H):void 0,u=!!(n&&s&&Da(s)&&d),h=u&&n&&s?{id:s.startedAt,game:s,completedAt:s.completedAt??new Date().toISOString(),expiresAt:new Date(Date.now()+7200*1e3).toISOString(),viewerPlayerId:n.playerId}:void 0;du(),Ct.current&&(window.clearTimeout(Ct.current),Ct.current=void 0),u||Ro(),Ma.current="",eo.current="",Wr.current="",So.current="",El.current="",Ze.current=void 0,Es.current+=1,As.current+=1,Nl.current=!1,Dl.current=!1,mi.current=0,lt.current?.close(1e3,"Room cleared"),lt.current=void 0,Rp.current=Promise.resolve(),cn(!1),ja.current=!1,Ne(void 0),d&&h&&(Vi(),gn(x=>[d,...x.filter(g=>g.id!==d.id)]),Wu(x=>Ym([h,...x])),Xc(h),Lt(!0),Zt(!1)),bo(void 0),ep(!1),Hu(!1),Lo(void 0),pi.current=void 0,G(a)}function Gr(a){return a==="Expired token"||a==="Invalid token"||a==="Room not found"||a==="Room closed"||a==="Removed from room"||a==="Room expired"||a==="Lobby idle timeout"||a==="Host reconnect timeout"}function yi(a){const n=Ze.current;return!!(n&&n.roomCode===a.roomCode&&n.playerToken===a.playerToken&&n.playerId===a.playerId)}function zs(a){return $A(a,navigator.onLine)}function Up(a){return a==="This is not the current room turn"||a==="This recording no longer matches the current room turn"||a==="It is not your turn"||a==="This clip is already claimed"||a==="Claim this clip before recording"||a==="Clip does not match the current room turn"||a==="Room game is already complete"||a==="Room game has not started"}function Ea(a){return a==="Lobby idle timeout"?r("Room closed because no game started within 20 minutes."):a==="Host reconnect timeout"?r("Room closed because the host did not reconnect within 5 minutes."):a==="Room closed"?"Room closed by host.":a==="Removed from room"?"Removed from room.":"Room session expired. Create or join again."}function vi(a){return a==="Lobby idle timeout"?r("Room closed because no game started within 20 minutes."):a==="Host reconnect timeout"?r("Room closed because the host did not reconnect within 5 minutes."):a==="Room closed"?"Room closed":a==="Removed from room"?"Removed from room":"Room session expired"}function vn(){const a=Rn();return a.find(n=>n.id===ds)??a.find(n=>n.id===Tr)??a.find(n=>n.id===ye)??a[0]}function Fp(){const a=vn(),n=Va(),s=a?.id===ye?n:void 0,d=s??(a?void 0:n),u=s?Bo(s):a?.summary,h=Wi(u?.title,a?.name||d?.title),x=a?.id||d?.id||h;if(!h||!x)return;const g=d?Os(d,a):{id:u?.workshopId,slug:u?.workshopSlug},S=d?Fi(qk(d.clips)):u?.characters;return{packId:x,packTitle:h,packSource:g?.id||g?.slug?"workshop":"local",workshopPackId:g?.id,workshopPackSlug:g?.slug,creator:Jk(u?.creator||d?.creator),clipCount:u?.clipCount??d?.clips.length,videoCount:u?.videoCount??(d?Id(d.clips):void 0),videoDurationSeconds:u?.videoDurationSeconds??(d?Qm(d.clips):void 0),characters:S,roleClaimEnabled:mt==="coop"&&ba==="role"}}
156async function AR(a){if(!(!v||v.status!=="lobby"||v.recovery||v.roleSelectionActive||v.hostPlayerId!==v.playerId))try{const n=await DM({roomCode:v.roomCode,playerToken:v.playerToken,lobbyPack:a});Ne(n)}catch(n){El.current="";const s=n instanceof Error?n.message:"Room mod sync failed.";if(Gr(s)){$r(Ea(s)),se(vi(s));return}G(s),se(s)}}async function IR(a,n){if(!(!v||sf)){cf(a);try{const s={roomCode:v.roomCode,playerToken:v.playerToken,role:a},d=n?await VM(s):await LM(s);Ne(d)}catch(s){const d=s instanceof Error?s.message:"Could not update role.";G(d),se(d)}finally{cf(void 0)}}}async function NR(a){if(!(!v||v.status!=="lobby"||v.hostPlayerId!==v.playerId||Xu)){hf(!0);try{const n=await UM({roomCode:v.roomCode,playerToken:v.playerToken,maxPlayers:a});Ne(n)}catch(n){const s=n instanceof Error?n.message:"Player limit update failed.";G(s),se(s)}finally{hf(!1)}}}function DR(a,n){const s=vn(),d=Os(a,s),u=n.players.length?n.players:[{id:n.playerId,name:is,isHost:!0,isReady:!0,joinedAt:new Date().toISOString(),lastSeenAt:new Date().toISOString()}];if(mt==="pvp"&&u.length>Ck)throw new Error(`PVP supports up to ${Ck} players. Switch to Co-op mode or ask players to leave.`);const h=mt==="coop"?ba:"round-robin",x=mt==="coop"&&h==="round-robin"&&el,g=x?pL(u):u,S=mt==="pvp"?a.clips.flatMap((k,R)=>u.map(C=>({index:R,clipId:k.id,title:k.title,caption:k.caption,playerId:C.id,playerName:C.name}))):h==="claim"?a.clips.map((k,R)=>({index:R,clipId:k.id,title:k.title,caption:k.caption})):h==="role"?pI(a.clips,g,n.roleClaims??[]):a.clips.map((k,R)=>{const C=g[R%g.length];return{index:R,clipId:k.id,title:k.title,caption:k.caption,playerId:C.id,playerName:C.name}});if(S.length>aD)throw new Error(mt==="pvp"?`This mod has too many clips for PVP with ${u.length} players. Switch to a smaller mod, fewer players, or Co-op mode.`:"This mod has too many clips for a room. Switch to a smaller mod.");return cI({packId:a.id,packTitle:a.title,packSource:d?"workshop":"local",workshopPackId:d?.id,workshopPackSlug:d?.slug,clipCount:a.clips.length,mode:mt,pvpFormat:mt==="pvp"?qu:"turns",assignmentMode:h,randomizePlayerOrder:x,allowRetakes:tl,currentAssignmentIndex:0,startedAt:new Date().toISOString(),takes:[],assignments:S},n.lobbyPack)}function Os(a,n){if(n?.summary?.workshopId||n?.summary?.workshopSlug)return{id:n.summary.workshopId,slug:n.summary.workshopSlug};const s=[...Yt,...pt].find(d=>{if(n&&Th(d,n))return!0;const u=Ye(d.title)===Ye(a.title),h=Ye(d.creator)===Ye(a.creator);return u&&h});return s?{id:s.id,slug:s.slug}:void 0}function Bp(a){return Le.find(n=>Bm(n,a))}async function Zf(a){try{const n=Xm(await nk());return es(n),n.find(s=>Bm(s,a))}catch{return}}function Us(a){return Rn().find(n=>Bm(n,a))}function $l(a,n){if(n.clipCount&&a.clips.length!==n.clipCount)return`Wrong mod version. The room expects ${n.clipCount} clips, but this local copy has ${a.clips.length}. Install the exact Workshop mod.`;const s=Ye(n.packTitle);if(s&&Ye(a.title)!==s)return"Wrong mod version. Install the exact mod selected by the host."}function Xf(a,n){if(a.clips.length!==n.clipCount)return`Wrong mod version. The room expects ${n.clipCount} clips, but this local copy has ${a.clips.length}. Install the exact Workshop mod.`;const s=new Set(a.clips.map(u=>u.id));if(n.assignments.find(u=>
156!s.has(u.clipId)))return"Wrong mod version. Your local copy does not match the room clips. Install the exact Workshop mod."}function LR(a=v){return a?.players.find(n=>n.id===a.playerId)}function VR(a=v){return!!(a?.players.length&&a.players.every(n=>!n.reconnectDeadlineAt&&(n.isHost||n.isReady)))}function Gl(a=v){const n=lI(a,mt,a?.recovery?void 0:Fp(),!!(a?.lobbyPack&&Us(a.lobbyPack)),r("Wait for disconnected players to reconnect"));if(n||!a||a.recovery||mt!=="coop"||ba!=="role")return n;if(!a.lobbyPack?.roleClaimEnabled)return"Syncing role selection";const s=a.lobbyPack.characters??[];if(!s.length)return"This mod has no roles to claim";const d=new Set((a.roleClaims??[]).map(u=>u.role.toLocaleLowerCase()));if(a.roleSelectionActive&&s.some(u=>!d.has(u.toLocaleLowerCase())))return"Claim every role before starting"}function Wp(a=v){return a?a.players.length>=2&&VR(a)?"All players are ready":"Waiting for players":""}function Qf(){if(v&&v.hostPlayerId!==v.playerId){const k=v.lobbyPack;if(!k)return{title:"Waiting for Host",typeLabel:"MOD"};const R=Fi(k.characters);return{title:io(k.packTitle),rawTitle:k.packTitle,typeLabel:"MOD",linesLabel:k.clipCount?`${k.clipCount} lines`:void 0,videoLabel:k.videoCount?`${k.videoCount} video`:void 0,creatorLabel:k.creator?`By ${k.creator}`:void 0,roles:R}}const a=vn(),n=a?.id===ye?H:void 0,s=n??(a?void 0:H),d=n?Bo(n):a?.summary,u=io(d?.title||a?.name||s?.title||"No Dub Mod Selected"),h=d?.clipCount??s?.clips.length??0,x=d?.videoCount??(s?Id(s.clips):0),g=Fi(d?.characters,qk(s?.clips??[])),S=Jk(d?.creator||s?.creator);return{title:u,rawTitle:d?.title||a?.name||s?.title,typeLabel:"MOD",linesLabel:h?`${h} lines`:void 0,videoLabel:x?`${x} video`:void 0,creatorLabel:S?`By ${S}`:void 0,roles:g,coverUrl:qD(s)}}function kn(a){return`${a.roomCode}:${a.playerId}:${a.game?.startedAt??""}`}function to(a=v){if(!a?.game||a.game.completedAt)return;const n=Fs(a);return n===void 0?void 0:a.game.assignments[n]}function Fs(a=v){const n=a?.game;if(!a||!n||n.completedAt)return;if(!mr(n))return n.currentAssignmentIndex<n.assignments.length?n.currentAssignmentIndex:void 0;const s=new Set(n.takes.filter(u=>u.playerId===a.playerId).map(u=>u.assignmentIndex)),d=n.assignments.findIndex((u,h)=>u.playerId===a.playerId&&!s.has(h));return d>=0?d:void 0}function Aa(a=v){const n=to(a);return!!(a&&n?.playerId===a.playerId)}function ql(a=v){const n=to(a);return!!(a?.game&&Pi(a.game)==="claim"&&n&&!n.playerId)}function $p(a,n){return a.clips.find(s=>s.id===n.clipId)??a.clips[n.index]}function zR(a){return`${kn(a)}:${Fs(a)??"waiting"}:${a.game?.completedAt??""}`}function OR(a){const n=v?.game,s=to(v);return T==="multi"&&v&&n&&Ge(n)==="pvp"&&s?`${kn(v)}:pvp:${s.index}:${s.clipId}`:a.data.roundId}function UR(){if(T!=="multi"||!v?.game||Ge(v.game)!=="pvp"||mr(v.game))return!1;const a=eb(v),n=a.find(s=>s.status==="active");return!!(n&&a[0]&&n.assignmentIndex!==a[0].assignmentIndex)}function eb(a=v){const n=a?.game,s=to(a);return!a||!n||Ge(n)!=="pvp"||mr(n)||!s?[]:n.assignments.map((d,u)=>({assignment:d,assignmentIndex:u})).filter(({assignment:d})=>d.clipId===s.clipId&&d.index===s.index).map(({assignment:d,assignmentIndex:u})=>{const h=n.takes.some(g=>g.assignmentIndex===u),x=u<n.currentAssignmentIndex||h?"done":u===n.currentAssignmentIndex?"active":u===n.currentAssignmentIndex+1?"next":"waiting";return{...d,assignmentIndex:u,status:x,isSelf:d.playerId===a.playerId}})}async function pr(a,n,s=!1){if(a.status!=="playing"||!a.game)return;const d=zR(a);if(s&&(eo.current="",Wr.current=""),GA(eo.current,d,!!ke.current)){Jl(a,n??H);return}if(eo.current===d&&(eo.current=""),Wr.current===d){Jl(a,n??H);return}Wr.current=d,al(!0);try{await FR(a,n)&&(eo.current=d)}finally{Wr.current===d&&(Wr.current=""),al(!1)}}async function FR(a,n){const s=a.game;if(!s)return!1;us(Ge(s)),ef(Ub(s)),ps(Pi(s)),tf(!!s.randomizePlayerOrder),of(am(s));let d=Bp(s);d||(d=await Zf(s)),d&&(d=await rb(d,s));const u=n??(H&&_k(H,s)?H:void 0)??(d?await hr(d):void 0);if(!u)return await BR(a),!1;const h=Xf(u,s);if(h)return bo(void 0),So.current="",G(h),se(h),!1;if(So.current="",bo(void 0),Ma.current!==kn(a)){Ro();const C=xn(s.assignments.map(A=>A.index),u.clips.length);Xn(C),Lt(!1),Ma.current=kn(a);const U=await Of("multi",u,C,to(a)?.index??C[0]??0,d,a);if(U){const A=await Ip(U).catch(()=>({}));Object.keys(A).length&&(je.current=A,Vo(A))}}if(Jl(a,u),Da(s))return Vi(),Zt(!1),Lt(!0),Ir(!1),te(void 0),Cn(),G("Room run complete."),P("Room run complete."),
156!0;const g=to(a);if(mr(s)&&!g)return Zt(!1),Lt(!1),te(void 0),Cn(),Y("multi"),G("You finished. Waiting for the other racers."),P("Finished. Waiting for the other racers."),!0;const S=g?$p(u,g):void 0;if(!g||!S)return G("Current room clip is missing locally."),!1;Lt(!1),va(g.index),zo.current?.pause();const k=Fs(a);return await Bs(S,g.index,!0,$s(k,g.clipId))?(Zt(!0),Y("multi"),G(ql(a)?"Open line. Any player can take this clip.":Aa(a)?mr(s)?"Your race is in progress.":"Your turn.":`${g.playerName??"Player"}'s turn.`),!0):!1}async function BR(a){const n=a.game;if(!n)return;const s=WR(a);if(So.current===s)return;So.current=s;const d=await $R(n);if(d){bo({key:s,roomCode:a.roomCode,packTitle:n.packTitle,source:"workshop",workshopPack:d}),G(`Install "${n.packTitle}" from Workshop to play this room.`);return}bo({key:s,roomCode:a.roomCode,packTitle:n.packTitle,source:"local"}),G(`Host is using a local-only mod: ${n.packTitle}.`)}function WR(a){const n=a.game;return`${a.roomCode}:${n?.startedAt??""}:${n?.packId??""}:${n?.workshopPackId??""}:${n?.workshopPackSlug??""}`}async function tb(a){if(a.packSource!=="workshop"&&!a.workshopPackId&&!a.workshopPackSlug)return;const n=ab(a,[...Yt,...pt]);if(n)return n;try{const s=await zd({id:a.workshopPackId,slug:a.workshopPackSlug});return s&&Hl(s),s}catch{return}}async function $R(a){if(a.packSource!=="workshop"&&!a.workshopPackId&&!a.workshopPackSlug)return;const n=ab(a,[...Yt,...pt]);if(n)return n;try{const s=await zd({id:a.workshopPackId,slug:a.workshopPackSlug});return s&&Hl(s),s}catch{return}}async function ob(a,n){if(!ee||a.packSource!=="workshop"&&!a.workshopPackId&&!a.workshopPackSlug)return!0;const s=n?.id??a.workshopPackId,d=n?.slug??a.workshopPackSlug;if(pt.some(g=>!!(s&&g.id===s)||!!(d&&Ye(g.slug)===Ye(d)))||n?.subscribed)return!0;if(!s&&!d)return G("This room mod could not be linked to Workshop."),se("Workshop mod unavailable"),!1;const h=s??`slug:${d}`,x=new Set(Vr.current);x.add(h),Vr.current=x,qc(x),G(`Adding ${io(a.packTitle??n?.title??"room mod")} to your Workshop library.`);try{return await Fy(d?{slug:d}:{packId:s}),n?Hl({...n,subscribed:!0}):Ru(g=>g+1),!0}catch(g){const S=g instanceof Error?g.message:"Workshop subscription failed.";return G(S),se(S),!1}finally{const g=new Set(Vr.current);g.delete(h),Vr.current=g,qc(g)}}async function rb(a,n){const s=n.workshopPackId??a.summary?.workshopId,d=n.workshopPackSlug??a.summary?.workshopSlug;if(!a.summary||!s&&!d||a.summary.workshopId===s&&a.summary.workshopSlug===d)return a;const u={...a,summary:{...a.summary,workshopId:s,workshopSlug:d}};es(h=>h.map(x=>x.id===u.id?u:x));try{await ZA(u)}catch{}return u}function ab(a,n){return n.find(s=>a.workshopPackId&&s.id===a.workshopPackId?!0:!!(a.workshopPackSlug&&Ye(s.slug)===Ye(a.workshopPackSlug)))}function Hl(a){Pr(n=>n.some(s=>s.id===a.id)?n.map(s=>s.id===a.id?a:s):[a,...n]),Do(n=>a.subscribed?n.some(s=>s.id===a.id)?n.map(s=>s.id===a.id?a:s):[a,...n]:n.filter(s=>s.id!==a.id))}function Jl(a,n=H){const s=a.game;!s||!n?.clips.length||(Vo(d=>{const u={...d};if(s.lastRedoAt&&Number.isFinite(s.lastRedoAssignmentIndex)){const h=`${kn(a)}:${s.lastRedoAt}:${s.lastRedoAssignmentIndex}`;if(Mf.current!==h){const x=Math.round(Number(s.lastRedoAssignmentIndex)),g=s.assignments[x];g&&(delete u[$s(x,g.clipId)],Ge(s)==="coop"&&delete u[g.clipId]),Mf.current=h}}for(const h of s.takes){const x=s.assignments[h.assignmentIndex],g=x?$p(n,x):n.clips.find(C=>C.id===h.clipId);if(!g)continue;const S=$s(h.assignmentIndex,h.clipId),k=u[S],R={clipId:g.id,playerUrl:k?.playerAudio?k.playerUrl:h.audioDataUrl,playerAudio:k?.playerAudio,playerFeatures:k?.playerFeatures,score:h.scoreTotal!==void 0?ow(h):k?.score};u[S]=R,Ge(s)==="coop"&&(u[g.id]=R)}return je.current=u,u}),Uf(a))}async function Bs(a,n=xt,s=tt,d=a.id,u,h="server"){const x=++Cp.current;xo.current+=1,Zl(),fe.current?.pause(),Pt.current?.pause(),ir.current?.pause(),zo.current?.pause(),ht.current?.pause(),P("Preparing local clip."),qr(),_r(!0),ot(0),Vl(),nr(!1),vo(!1),yp.current=d,va(n);const g=u??(h==="local"?Mp():await hi()),{referenceAudio:S,referenceFeatures:k}=await Tp(a);if(x!==Cp.current)return!1;const R={title:a.title,caption:a.caption,duration:k.duration},C=h==="local"?$E({sessionId:g.sessionId,clip:R}):await Ny({sessionId:g.sessionId,packId:"local",localClip:R});if(x!==Cp.current)return!1;te({data:C,sessionId:g.sessionId,execution:h,speakerNames:a.speakers,referenceAudioUrl:a.audioUrl,referenceImageUrl:a.imageUrl,referenceVideoUrl:a.videoUrl,videoStartSeconds:a.videoStartSeconds,videoEndSeconds:a.videoEndSeconds,referenceAudio:S,referenceFeatures:k,localReferenceFeatures:k});const U=je.current[d];return Rr(U?.playerFeatures),_t(U?.playerUrl),la(U?.score),vo(!!(U?.playerFeatures||U?.playerUrl)),P(s?`Clip ${n+1} ready.`:"Local Pack / Unverified Reference"),Vt(!1),Hx(n),!0}async function Gp(){if(ve||ja.current)return;if(!q){P("No round is active.");return}if(!dm()){P(ql(v)?"Take this line first.":"Wait for your turn.");return}if(ri.current)return;xf.current=T==="multi"?v:void 0,Xt.current?.cancel(),Rf(void 0),oo(),wa.current?.pause();const a=rv();ri.current=!0,ws.current="permission",si.current=void 0,To(void 0),oa(void 0),lo(!0),uo(!1),P("Connecting microphone...");try{const n=await Gf();if(!n){a?.close(),uo(!0),await Ol("permission",lp.current);return}const s=new av(n,Vf,{audioContext:a,preferMediaRecorder:yt(),onStartupStage:qf,onRuntimeFailure:u=>{Ul(u,n,"recording")}});He.current=s;try{await s.start()}catch(u){s.cancel(),He.current=void 0,uo(!0),await Ol(ws.current,u,n);return}cr.current=!1,qr(),vo(!0),Pt.current?.pause(),ir.current?.pause(),ht.current?.pause(),ot(0),Sa(!1),nr(!1),Vl();const d=Ib();xP(d,Xr),uo(!1),It(!0),P("Get ready."),yl.current=window.setTimeout(()=>{if(!sr.current||cr.current)return;q.referenceVideoUrl&&fe.current&&(Sn(),fe.current.muted=!0,fe.current.play().catch(()=>{}));const u=aP();u&&rP(),P(u?"Recording with original guide.":"Recording.")},Math.max(0,Xr*1e3))}finally{ri.current=!1,lo(!1)}}async function GR(){if(!v?.game){P("Room game has not started.");return}if(Ju)return;const a=to(v);if(!a){P("No current room clip.");return}nf(!0),P("Taking this line."),G("Taking this line.");try{const n=await $M({roomCode:v.roomCode,playerToken:v.playerToken,assignmentIndex:v.game.currentAssignmentIndex,clipId:a.clipId});Ne(n),G("This line is yours."),P("This line is yours."),await pr(n,H)}catch(n){const s=n instanceof Error?n.message:"Could not take this line.";zs(s)?(P("Connection interrupted. Try taking this line again."),G("Connection interrupted. Try taking this line again.")):Up(s)?(P("Syncing latest room turn."),G("Syncing latest room turn."),await bi(v,!1)):(P(s),G(s))}finally{nf(!1)}}async function qp(){if(!v?.game){P("Room game has not started.");return}if(ln||ae)return;const a=to(v);if(!a){P("No current room clip.");return}if(!(a.playerId===v.playerId||v.hostPlayerId===v.playerId)){P("Only the player who took this line or the host can release it.");return}lf(!0),P("Releasing this line."),G("Releasing this line.");try{const s=await GM({roomCode:v.roomCode,playerToken:v.playerToken,assignmentIndex:v.game.currentAssignmentIndex,clipId:a.clipId});a.playerId===v.playerId&&rd(),Ne(s),G("Line released."),P("Line released."),await pr(s,H)}catch(s){const d=s instanceof Error?s.message:"Could not release this line.";zs(d)?(P("Connection interrupted. Try releasing this line again."),G("Connection interrupted. Try releasing this line again.")):Up(d)?(P("Syncing latest room turn."),G("Syncing latest room turn."),await bi(v,!1)):(P(d),G(d))}finally{lf(!1)}}async function qR(){if(!(!v?.game||rl||ae)){if(Ge(v.game)!=="coop"||v.hostPlayerId!==v.playerId){G("Only the C
156o-op host can redo the last line.");return}df(!0),P("Returning to the previous line."),G("Returning to the previous line.");try{const a=await qM({roomCode:v.roomCode,playerToken:v.playerToken});Ne(a),Lt(!1),G("Previous line reopened."),P("Previous line reopened."),await pr(a,H)}catch(a){const n=a instanceof Error?a.message:"Could not redo the last line.";P(n),G(n)}finally{df(!1)}}}async function HR(){if(!(!v?.game||_u||ae)){if(Ge(v.game)!=="coop"||v.hostPlayerId!==v.playerId){G("Only the Co-op host can skip the current line.");return}uf(!0),P("Skipping the current line."),G("Skipping the current line.");try{const a=await HM({roomCode:v.roomCode,playerToken:v.playerToken});rd(),Ne(a),G(Da(a.game)?"Final line skipped. Room run complete.":"Line skipped."),P(Da(a.game)?"Room run complete.":"Line skipped."),await pr(a,H)}catch(a){const n=a instanceof Error?a.message:"Could not skip the current line.";P(n),G(n)}finally{uf(!1)}}}async function JR(){const a=v,n=a?.game;if(!(!a||!n||Yu)){if(a.hostPlayerId!==a.playerId){P("Only the host can export the room recovery.");return}if(n.mode!=="coop"||n.assignmentMode!=="claim"||n.completedAt){P("Recovery export is available during an active Co-op Claim Mode game.");return}pf(!0),P("Saving the latest room progress."),G("Preparing Recovery ZIP.");try{Uf(a),await up.current;const s=Te.current;if(!s||s.roomCode!==a.roomCode||s.roomRunKey!==kn(a))throw new Error("The room recovery snapshot is not ready yet. Keep the room open and try again.");const d=await fc(s.id),u=await _w(s,d),x=await Zw(u,Kw(s))==="cancelled"?"Recovery ZIP save cancelled. The room is still active.":"Recovery ZIP saved. You can import it later from the multiplayer page.";P(x),G(x)}catch(s){const d=s instanceof Error?s.message:"Recovery ZIP could not be created.";P(d),G(d)}finally{pf(!1)}}}function _R(a){const n=Zd(a);Ra.current=n,zx(n),Sb(),wn(),zl(),bn.current={},Sl.current={},Ca.current={},Ps.current={},ci.current={},Rl.current={},zr.current={},li.current={},Pl.current={},Ur.current={},wo.current="",zt.current=""}function nb(a,n){return JSON.stringify([n.roomId,$t(n.game),a.playerUrl])}function Hp(a,n,s=!1){const d=Xt.current?.announcement();if(!(!d||!s&&!Er||a.readyState!==WebSocket.OPEN||d.value.room.roomId!==n.roomId||d.value.turnKey!==$t(n.game)||fn.current?.socket===a&&fn.current.id===d.id))try{a.send(JSON.stringify({type:"take-preview-available",previewId:d.id,turnKey:d.value.turnKey})),fn.current={socket:a,id:d.id}}catch{fn.current=void 0}}function YR(){const a=Xt.current?.announcement();Xt.current?.cancel();const n=lt.current;if(a&&n?.readyState===WebSocket.OPEN&&fn.current?.socket===n&&fn.current.id===a.id)try{n.send(JSON.stringify({type:"take-preview-unavailable",previewId:a.id,turnKey:a.value.turnKey}))}catch{}fn.current=void 0}function KR(a,n){const s=$t(n?.game),d=Ze.current;if(!n?.takePreviewSupported||!s||!a.playerAudio||!Aa(n)||d?.roomId!==n.roomId||$t(d.game)!==s)return;Xt.current??=new rM({canShare:({room:h,turnKey:x})=>{const g=Ze.current;return g?.status==="playing"&&g.roomId===h.roomId&&$t(g.game)===x&&!ja.current},encode:({take:h})=>Dd(En(bk(h.playerAudio))),upload:async({room:h,turnKey:x},g,S)=>{const k=h.game.currentAssignmentIndex;return(await WM({roomCode:h.roomCode,playerToken:h.playerToken,assignmentIndex:k,clipId:h.game.assignments[k].clipId,turnKey:x,previewId:g,audioDataUrl:S})).preview},changed:({take:h,room:x,turnKey:g},S,k)=>{Rf({playerUrl:h.playerUrl,state:S}),k&&Ne(R=>R?.roomId===x.roomId&&$t(R.game)===g?{...R,takePreview:k}:R)}}),Xt.current.stage(nb(a,n),{take:a,room:n,turnKey:s});const u=lt.current;u&&Ze.current&&Hp(u,Ze.current)}function oo(){dp.current++;const a=zo.current;zo.current=null,a&&(a.onended=null,a.ontimeupdate=null,a.pause(),fe.current?.pause(),ht.current?.pause()),ml.current&&URL.revokeObjectURL(ml.current),ml.current=void 0,pl("idle")}function ib(){const a=xa.current;a&&window.clearTimeout(a.timer),xa.current=void 0}async function sb(a=!1){const n=md(v),s=vm(v);if(!n&&!s||!q||Aa(v))return;if(Ss!=="idle"&&!a){ib(),oo();return}if(!n&&s){const h=lt.current;if(!h||h.readyState!==WebSocket.OPEN){P("Room connection is unavailable. Try again shortly.");return}oo();const x=window.setTimeout(()=>{xa.current?.previewId===s.previewId&&(xa.current=void 0,oo(),P("The take could not be shared. Try Listen to Take again."))},3e4);xa.current={previewId:s.previewId,turnKey:s.turnKey,timer:x},pl("loading");try{h.send(JSON.stringify({type:"take-preview-request",previewId:s.previewId,turnKey:s.turnKey}))}catch{ib(),oo(),P("Room connection is unavailable. Try again shortly.")}return}if(!n)return;oo();const d=dp.current,u=()=>dp.current===d&&md(Ze.current)?.previewId===n.previewId;qr(),Pt.current?.pause(),ir.current?.pause(),fe.current?.pause(),ht.current?.pause(),Sa(!1),nr(!1),pl("loading");try{const h=await Dw(n.audioDataUrl);if(!u())return;const x=URL.createObjectURL(new Blob([h.bytes],{type:h.mimeType||"audio/wav"}));ml.current=x;const g=new Audio(x);zo.current=g,g.volume=Math.max(0,Math.min(1,$e));const S=q.referenceVideoUrl?fe.current:null;if(S){if(S.muted=!0,S.playbackRate=1,await Ha(S),!u())return;Sn(),await mc(S)}const k=H?.backingTrackUrl?ht.current:null;if(k){if(await zk(k),!u())return;const R=Math.max(0,q.videoStartSeconds??Ut()?.videoStartSeconds??0);k.currentTime=Number.isFinite(k.duration)?Math.min(R,Math.max(0,k.duration-.05)):R,k.volume=Rm($e,Ra.current)}if(!u())return;if(g.onended=()=>{u()&&(oo(),ot(1))},g.ontimeupdate=()=>{u()&&ot(Sd(g.currentTime,Number.isFinite(g.duration)?g.duration:Ws(),Xr))},await Promise.all([g.play(),S?.play().catch(()=>{}),k?.play().catch(()=>{})]),!u()){g.pause();return}pl("playing"),P(`Playing ${n.playerName}'s latest take.`)}catch{u()&&(oo(),P("Could not play this take. Tap Listen to Take to try again."))}}function ZR(){if(!v?.takePreviewSupported||Ge(v.game)!=="coop")return null;const a=md(v),n=vm(v),s=a?.playerName??n?.playerName;return t.jsxs("button",{type:"button",className:`${a||n?"action-button":"ghost-button"} shared-take-preview-button`,disabled:Aa(v)||!a&&(!n||!Er)||ve,"aria-label":s?`Listen to ${s}'s latest take`:"Waiting for a recorded take","aria-pressed":Ss==="playing",title:s?`${s}'s latest take`:"Available when the player finishes recording",onClick:()=>{sb()},children:[Ss==="idle"?t.jsx(bm,{size:18}):t.jsx(pd,{size:18}),Ss==="loading"?"Loading...":Ss==="playing"?"Stop Take":"Listen to Take"]})}function XR(){return!v?.takePreviewSupported||Ge(v.game)!=="coop"||!ni||ni.playerUrl!==Ce||ni.state==="waiting"?null:ni.state==="failed"?t.jsx("button",{className:"ghost-button room-preview-upload-status",type:"button",disabled:ve,onClick:()=>Xt.current?.retry(),children:"Retry sharing take"}):t.jsx("span",{className:"room-preview-upload-status",role:"status",children:ni.state==="uploading"?"Sharing take...":"Take shared with the room"})}async function QR(){oo();const a=ir.current;if(!a||!Ce){P("Record this clip first.");return}qr(),Pt.current?.pause();const n=H?.backingTrackUrl?ht.current:null;n?.pause(),Sa(!1),nr(!0);const s=fe.current,d=q?.referenceVideoUrl;if(s?.pause(),s&&d&&(s.muted=!0,s.playbackRate=1,await Ha(s),Sn(),await mc(s)),a.currentTime=0,a.volume=Math.max(0,Math.min(1,$e)),n){await zk(n);const u=Math.max(0,q?.videoStartSeconds??Ut()?.videoStartSeconds??0),h=Number.isFinite(n.duration)?Math.max(0,n.duration-.05):u;n.currentTime=Math.min(u,h),n.volume=Rm($e,Ra.current)}ot(Sd(0,tm(a),Xr)),P(n?"Playing your take with background audio.":"Playing your take.");try{const u=a.play(),h=n?.play().catch(()=>{}),x=s&&d?s.play().catch(()=>{}):void 0;await u,await h,await x}catch{s?.pause(),n?.pause(),nr(!1),P("Could not play this take.")}}async function Jp(a="manual"){const n=He.current;if(!n||cr.current)return;cr.current=!0;const s=kl.current;_r(!1);let d;try{d=await n.stop()}catch(de){He.current=void 0,cr.current=!1,fe.current?.pause(),It(!1);const ie=await eL(de,b.stream);oa(ie),To({...ie,source:"take-processing"}),uo(!1),P(ie.message);return}if(d.health.runtimeFailure){He.current=void 0,cr.current=!1,fe.current?.pause(),It(!1),await Ul(d.health.runtimeFailure,b.stream,"recording");return}if(d.health.levelSampleCount>=3&&d.health.maxPeak<=1e-6){const de=b.stream?.getAudioTracks()[0],ie={code:"digital-silence",message:"The browser recorded digital silence instead of microphone audio.",elapsedMs:Math.round(d.samples.length/Math.max(1,d.sampleRate)*1e3),lastAudioDataAgeMs:0,audioFrameCount:d.health.audioFrameCount,levelSampleCount:d.health.levelSampleCount,maxPeak:d.health.maxPeak,trackReadyState:de?.readyState??"missing",trackEnabled:de?.enabled??!1,trackMuted:de?.muted??!1,audioContextState:"missing",recorderMode:d.health.recorderMode};He.current=void 0,cr.current=!1,fe.current?.pause(),It(!1),await Ul(ie,b.stream,"recording");return}He.current=void 0,cr.current=!1,fe.current?.pause(),It(!1);const u=Ws(),h=CD(d,s),x=h0(h,Jt),g=vj(x,u),S=ze,k=$y(Ee),R=Ni(Ee),C=R?qy(g.samples,g.s
156ampleRate,S,k):{samples:new Float32Array(g.samples),sampleRate:g.sampleRate},U=R?Hy(g,S,k):{samples:new Float32Array(g.samples),sampleRate:g.sampleRate},A=Xj(C,U,mn),F=Sm(A.scoringAudio.samples,A.scoringAudio.sampleRate),$=Tn(A.playbackAudio);Ce&&URL.revokeObjectURL(Ce),Rr(F),_t($),la(void 0);const Q=Ut(),J=Na(Q);let le=!1;if(Q&&J){const de=je.current[J];de?.playerUrl&&de.playerUrl!==$&&URL.revokeObjectURL(de.playerUrl);const ie={clipId:Q.id,playerUrl:$,playerAudio:A.playbackAudio,playerFeatures:F},Pe={...je.current,[J]:ie};je.current=Pe,Vo(Pe),KR(ie,xf.current),le=await Ap(J,ie)}a==="time-limit"&&(ll(Ib()),ot(1)),vo(!0),P(tt?a==="time-limit"?`Time up.${le?" Saved locally.":""} Save to continue.`:`Take ready.${le?" Saved locally.":""} Save to continue.`:d.fallbackActive?"Fallback Recorder Active":"Recording processed.")}async function cb(){if(!q||!Oe){P("Score unavailable.");return}if(P("Scoring."),q.execution==="local"){if(!q.localReferenceFeatures){P("Score unavailable.");return}let u;try{u=GE(q.localReferenceFeatures,Oe)}catch(h){P(h instanceof Error?`Score failed: ${h.message}`:"Score failed.");return}return await lb(u),u}const a=await hi(),n=await db(a,q);if(!n)return;const s=u=>wM({sessionId:a.sessionId,roundId:u.data.roundId,roundToken:u.data.roundToken,playerFeatures:Oe,localReferenceFeatures:u.localReferenceFeatures});let d;try{d=await s(n)}catch(u){if(MP(u)){P("Refreshing score.");const h=await db(a,n,{force:!0});if(h)try{d=await s(h)}catch(x){P(x instanceof Error?`Score failed: ${x.message}`:"Score failed.");return}}if(!d){P(u instanceof Error?`Score failed: ${u.message}`:"Score failed.");return}}if(d)return await lb(d),d}async function lb(a){la(a);const n=Ut(),s=Na(n);if(n&&s){const d=je.current[s],u={...je.current,[s]:{clipId:n.id,playerUrl:d?.playerUrl??Ce??"",playerAudio:d?.playerAudio,playerFeatures:d?.playerFeatures??Oe,score:a}};je.current=u,Vo(u),await Ap(s,u[s])}}async function db(a,n,s={}){if(!s.force&&!CP(n,a))return n;const d=Ut();if(!d||!n.localReferenceFeatures){P("Could not refresh this line. Start this line again.");return}P(Vb(n)?"Refreshing clip.":"Refreshing score.");const u=await Ny({sessionId:a.sessionId,packId:"local",localClip:{title:d.title,caption:d.caption,duration:n.localReferenceFeatures.duration}}),h={...n,data:u,sessionId:a.sessionId};return te(h),h}async function ub(){if(!q){P("Record this clip first.");return}if(T==="multi"&&v?.game){const d=to(v),u=Ut(),h=Na(u),x=h?je.current[h]:void 0;if(!d||!u||!h||!x?.playerAudio){P("Record this turn first."),Vt(!1);return}if(!Aa(v)){P(ql(v)?"Take this line first.":"Wait for your turn."),Vt(!1);return}if(ja.current){P("Saving turn to room.");return}ja.current=!0,Vt(!0);try{const g=Ke??(Oe?await cb().catch(()=>{}):void 0);P("Saving turn to room.");const S=bk(x.playerAudio),k=await Xt.current?.forSubmission(nb(x,v)),R=k?.preview,C=Fs(v);if(C===void 0){P("This race is already finished for you.");return}let U;R||(U=await Dd(En(S)));const A={roomCode:v.roomCode,playerToken:v.playerToken,assignmentIndex:C,clipId:d.clipId,score:g,turnKey:$t(v.game)};let F;try{F=await zy({...A,audioDataUrl:U,previewId:k?.previewId})}catch(Q){if(!k?.previewId||!(Q instanceof Error)||!Q.message.includes("shared preview is no longer available"))throw Q;U=await Dd(En(S)),F=await zy({...A,audioDataUrl:U})}const $=F.game?.takes.find(Q=>Q.assignmentIndex===C);if(Vo(Q=>{const J={...Q},le={...J[h]??x,playerAudio:S,playerUrl:$?.audioDataUrl??U??x.playerUrl,score:g??x.score};return J[h]=le,Ge(F.game)==="coop"&&(J[u.id]=le),je.current=J,J}),Te.current&&F.game){const Q=to(F)?.index??xt,J=await ac(Te.current.id,{currentClipIndex:Q,status:F.game.completedAt?"complete":"active",roomGame:F.game}).catch(()=>{});J&&(Te.current=J)}Ne(F),G("Next turn."),zo.current?.pause(),await pr(F,H)}catch(g){const S=g instanceof Error?g.message:"Save turn failed.";if(zs(S)){P("Connection interrupted. Your take is still here. Try Next again."),G("Connection interrupted. Your take is still here. Try Next again.");return}if(Up(S)){P("Syncing latest room turn."),G("Syncing latest room turn."),await bi(v,!1);return}P(S),G(S),Gr(S)&&$r(Ea(S))}finally{ja.current=!1,Xt.current?.releaseSubmission(),Vt(!1)}return}if(!Oe){P("Record this clip first.");return}Vt(!0);const a=Ke??await cb();if(!a||!H){Vt(!1);return}const n=La(H.clips.length);if(n.indexOf(xt)>=n.length-1){await tP(a);return}P(`Clip ${Bb()+1} saved.`),await Qx()}async function eP(a){if(T!=="single"||!tt||ae||ve||pn||!H)return;const n=La(H.clips.length),s=n.indexOf(xt),d=n[a],u=d===void 0?void 0:H.clips[d];if(!(s<0||a<=s||!u)){tp(!0),Vt(!0),qr(),Pt.current?.pause(),fe.current?.pause();try{const h={...je.current};for(let x=s;x<a;x+=1){const g=n[x],S=g===void 0?void 0:H.clips[g];if(!S)continue;
156const k=S.id;let R=h[k];if(!R?.playerAudio&&!R?.playerUrl){const C=g===xt&&q?.referenceAudio?q.referenceAudio:(await Tp(S)).referenceAudio,U=Lk(C);R={clipId:S.id,playerUrl:Tn(U),playerAudio:U,keptOriginal:!0},h[k]=R,await Ap(k,R)}}if(je.current=h,Vo(h),Cn(),Te.current){const x=await ac(Te.current.id,{currentClipIndex:d}).catch(()=>{});x&&(Te.current=x)}gs(!1),un(void 0),P(`Skipped to clip ${a+1}.`),va(d),Ep(u),await Bs(u,d,!0,u.id,void 0,"local")}catch(h){Vt(!1),P(h instanceof Error?`Could not skip clips: ${h.message}`:"Could not skip clips.")}finally{tp(!1)}}}async function tP(a){Vi();const n=a??dL(),s=tR(n);if(Te.current){const d=await ac(Te.current.id,{status:"complete",currentClipIndex:xt,finalScore:s?.score??n}).catch(()=>{});d&&(Te.current=d)}if(s&&!Bt()){const d=hP(s);Ur.current[s.id]=d}else s&&wn();rR(a,s),s&&!Bt()&&Ia(s),Ns(),Qn(!1),dn(!1),Cl.current="",Lt(!0),Zt(!1),Ir(!1),te(void 0),Cn(),P(s?"Run complete. Export saved.":"Run complete."),Vt(!1)}async function ki(a=ke.current){oo();const n=Pt.current;if(!n||!a?.referenceAudioUrl){vo(!0);return}const s=++xo.current;ir.current?.pause(),ht.current?.pause(),fe.current?.pause(),n.pause(),n.currentTime=0,n.volume=Math.max(0,Math.min(1,$e)),ot(Si().contentStartProgress),Sa(!0),nr(!1),a.referenceVideoUrl&&await Tb(a,s),!(!Qp(a,s)||Pt.current!==n)&&(gb(em(n),s),P("Playing original line."),await n.play().catch(d=>{Qp(a,s)&&(wi(s),P(d instanceof Error?`Original playback failed: ${d.message}`:"Original playback failed."))}))}function pb(a){!Fo(a.currentTarget)||sr.current||wi()}function mb(a){if(!Fo(a.currentTarget)||ke.current?.referenceAudioUrl)return;const n=Pt.current,s=n&&!n.paused&&!n.ended,d=ir.current,u=d&&!d.paused&&!d.ended||zo.current&&!zo.current.paused&&!zo.current.ended;s||u||wi()}function wi(a){a!==void 0&&a!==xo.current||(xo.current+=1,Zl(),qr(),fe.current?.pause(),Pt.current?.pause(),Sa(!1),ot(Si().contentEndProgress),vo(!0))}function hb(){!q||hs||ae||!Is.current||performance.now()>=Is.current-50&&wi()}function oP(){const a=Pt.current,n=fe.current;return!!(Is.current||a&&!a.paused&&!a.ended||n&&!n.paused&&!n.ended)}function qr(){Ll.current&&(window.clearTimeout(Ll.current),Ll.current=void 0),Is.current=0}function gb(a=Ql(),n=xo.current){qr();const s=Math.max(.25,Math.min(30,a));Is.current=performance.now()+s*1e3+700,Ll.current=window.setTimeout(()=>{n===xo.current&&hb()},s*1e3+700)}function fb(a){if(!Fo(a)||sr.current)return;const n=ke.current,s=fe.current;if(n?.referenceVideoUrl&&s&&Fo(s)){const g=Math.max(0,(n.videoStartSeconds??0)+a.currentTime),S=n.videoEndSeconds,k=S===void 0?g:Math.min(g,S);if(s.paused||Math.abs(s.currentTime-k)>.22){try{s.currentTime=k}catch{}s.play().catch(()=>{})}}const d=em(a),u=Math.max(0,Math.min(1,a.currentTime/d)),h=Si(),x=h.contentStartProgress+u*(h.contentEndProgress-h.contentStartProgress);ot(x),u>=.995&&!sr.current&&wi()}function rP(){const a=Pt.current;!a||!q?.referenceAudioUrl||(a.pause(),a.currentTime=0,a.volume=Math.max(0,Math.min(1,$e*dD)),a.play().catch(()=>{P("Recording. Original guide playback was blocked by the browser.")}))}function aP(){return ar&&(T==="single"||T==="multi"&&Ge(v?.game)==="coop")}function nP(){const a=Pt.current;a&&(a.pause(),a.currentTime=0)}function bb(){if(ae||ut)return;const a=!ar;Ox(a);try{if(localStorage.setItem($S,a?"1":"0"),a&&localStorage.getItem(Tk)!=="1"){localStorage.setItem(Tk,"1");const s="Original guide enabled. Use headphones so it is not recorded by your microphone.";P(s),T==="multi"&&G(s);return}}catch{}const n=a?"Original guide enabled for recording.":"Original guide disabled.";P(n),T==="multi"&&G(n)}function yb(a){if(!Fo(a)||ke.current?.referenceAudioUrl)return;const n=ke.current?.videoStartSeconds??0,s=Ql(),d=Math.max(0,Math.min(1,(a.currentTime-n)/s)),u=Si(),h=u.contentStartProgress+d*(u.contentEndProgress-u.contentStartProgress);ot(h),d>=.995&&wi()}async function _l(a=0){const n=fe.current,s=ro(),d=s?.clips??H?.clips??[],u=d.filter(k=>{const R=ko[k.id],C=s?.takes[k.id];return!!(R?.playerAudio||R?.playerUrl||C?.playerAudio||C?.playerUrl)}),h=d.find(k=>k.videoUrl),x=h?.videoUrl;if(!n||!h||!x||u.length===0){P("No completed dubbed work to play yet.");return}const g=Math.max(0,Number.isFinite(a)?a:0);Ri(),lr.current=!1,Me(!1),dr.current=!1;const S=s?Ca.current[s.id]??bn.current[s.id]:void 0;if(s&&S){const k=Wf(s.id,S);try{n.pause(),n.muted=!1,n.playbackRate=1,n.src!==k&&(n.src=k,n.load()),await Ha(n),n.currentTime=Math.min(g,Math.max(0,(Number.isFinite(n.duration)?n.duration:g)-.05)),await mc(n),await n.play(),Me(!0),P("Playing your rendered dubbed work.")}catch(R){n.pause(),Me(!1),P(R instanceof Error?`Dubbed playback failed: ${R.message}`:"Dubbed playback failed. Try downloading the video instead.")}return}n.pause(),zl(),n.src!==x&&(n.src=x,n.load()),n.muted=!0,n.playbackRate=1,lr.current=!0,dr.current=!!s;try{const k=new AudioContext;wl.current=k,await k.resume(),await Ha(n);const R=Number.isFinite(n.duration)&&n.duration>0?n.duration:g,C=Math.min(g,Math.max(0,R-.05));try{n.currentTime=C}catch{n.currentTime=0}await mc(n);const U=[],A=s?.backingTrackUrl??H?.backingTrackUrl;if(A){const $=await $a(k,A).catch(()=>{});$&&U.push({buffer:$,offsetSeconds:0,startOffsetSeconds:C,gain:Ra.current})}for(const $ of u){const Q=ko[$.id],J=s?.takes[$.id],le=Q?.playerAudio??J?.playerAudio,de=Q?.playerUrl??J?.playerUrl;if(!le&&!de)continue;const ie=le?Ed(k,le):await $a(k,de),Pe=Math.max(0,$.videoStartSeconds??0),dt=Math.max(0,C-Pe);dt>=ie.duration-.03||U.push({buffer:ie,offsetSeconds:Math.max(0,Pe-C),startOffsetSeconds:dt>0?dt:void 0,gain:1})}await n.play(),await Ok(n);const F=k.currentTime+.04;
156U.forEach(({buffer:$,offsetSeconds:Q,startOffsetSeconds:J,gain:le})=>{jP(k,$,F+Q,J??0,le)}),Me(!0),P("Playing your full dubbed work.")}catch(k){Ri(),lr.current=!1,Me(!1),n.pause(),P(k instanceof Error?`Video playback failed: ${k.message}`:"Video playback failed. This browser may not support this video.")}}function iP(a){const n=ro();!n||Br.current||lr.current||ne?.workId===n.id&&a.currentTarget.src===ne.url||!n.completedCount||!n.clips.some(s=>s.videoUrl)||(Br.current=!0,a.currentTarget.pause(),_l(a.currentTarget.currentTime).finally(()=>{window.setTimeout(()=>{Br.current=!1},0)}))}function sP(){const n=fe.current?.currentTime??0;Br.current=!0,_l(n).finally(()=>{window.setTimeout(()=>{Br.current=!1},0)})}function vb(a="Judges are ready."){Mb(),Qn(!0),dn(!1),P(a)}function cP(){vb("Judges are ready.")}function lP(){vb("Skipped to judges.")}async function kb(a=!1){const n=ro();if(!n){P("No completed dubbed video to download yet.");return}const s=Xo,d=s?bn.current[n.id]:Ca.current[n.id];if(d){Jr(d,n,"dub",s,!0);return}if(di.current){P("A video download is already starting.");return}xr(void 0);const u=vk();di.current=!0,ui.current=!0;try{if(!await xb())return;await Zp(n,!0,s,a,u)}finally{await kk(u),di.current=!1,ui.current=!1,!Ue.current.dub&&!Ue.current.shorts&&Sr(!1)}}async function wb(a=!1){const n=ro();if(!n){P("No completed Shorts video to download yet.");return}const s=Xo,d=s?ci.current[n.id]:zr.current[n.id];if(d){Jr(d,n,"shorts",s,!0);return}if(di.current){P("A video download is already starting.");return}xr(void 0);const u=vk();di.current=!0,ui.current=!0;try{if(!await xb())return;const h=s?void 0:Ur.current[n.id];if(h){P("Shorts video is still preparing.");const x=await at(h,Pk,"Background Shorts preparation did not finish in time.").catch(()=>{});if(x){Jr(x,n,"shorts",s);return}delete Ur.current[n.id],await wn()}await Xp(n,!0,s,a,u)}finally{await kk(u),di.current=!1,ui.current=!1,!Ue.current.dub&&!Ue.current.shorts&&Sr(!1)}}async function dP(){const a=ca,n=ro();if(!a||!n||a.workId!==n.id){P("This export is no longer available to retry.");return}if(a.format==="shorts"){await wb(!0);return}await kb(!0)}async function uP(){if(!ca){P("No video export error details to copy.");return}try{await hc(ca.details),P("Error details copied. Paste them into your report.")}catch{P("Could not copy the error details. Allow clipboard access and try again.")}}function yL(a,n,s,d){const u=Cs.current.catch(()=>{}).then(async()=>{const h=await pP(a);if(!(!h||h.discarded)&&!h.segmentKeys.has(d)){h.segmentKeys.add(d);try{await mP(h,n,s)}
156catch(x){throw h.segmentKeys.delete(d),x}}});Cs.current=u.catch(h=>{console.warn("Incremental Shorts render failed:",h)})}async function pP(a){if(Bt()||!("MediaRecorder"in window))return;const n=Or.current;if(n&&n.packId!==a.id)await wn();else if(n&&!n.discarded)return n;const s=document.createElement("canvas");s.width=Bd,s.height=Wd;const d=s.getContext("2d");if(!d||typeof s.captureStream!="function")return;const u=s.captureStream(30),h=new AudioContext,x=h.createMediaStreamDestination(),g=yk(h,x),S=new MediaStream([...u.getVideoTracks(),...x.stream.getAudioTracks()]),k=Wk(!1);let R;try{R=$k(S,k)}catch{S.getTracks().forEach(Q=>Q.stop()),await h.close().catch(()=>{});return}const C=document.createElement("video");C.crossOrigin="anonymous",C.muted=!0,C.defaultMuted=!0,C.playsInline=!0,C.setAttribute("playsinline",""),C.setAttribute("webkit-playsinline",""),C.preload="auto";const U=Uk(C),A=[],F=new Promise((Q,J)=>{R.ondataavailable=le=>{le.data.size>0&&A.push(le.data)},R.onerror=()=>J(new Error("Shorts recorder failed.")),R.onstop=()=>Q(new Blob(A,{type:R.mimeType||"video/webm"}))}),$={packId:a.id,packTitle:a.title,video:C,unmountVideo:U,canvas:s,context:d,audioContext:h,audioDestination:x,audioMasterInput:g,outputStream:S,recorder:R,chunks:A,stopped:F,segmentKeys:new Set,activeSources:[],renderedDurationSeconds:0};Or.current=$;try{return await at(h.resume(),Hm,"Background Shorts audio processing did not start."),a.backingTrackUrl&&($.backingBuffer=await at($a(h,a.backingTrackUrl),uc,"Background Shorts audio timed out.").catch(()=>{})),R.start(250),$.recorderStarted=!0,Bk(R),$}catch(Q){throw await _p($),Q}}async function mP(a,n,s){if(a.discarded||!n.videoUrl||!s.playerAudio&&!s.playerUrl||(await gP(a,n.videoUrl),a.discarded))return;const d=s.playerAudio?Ed(a.audioContext,s.playerAudio):await at($a(a.audioContext,s.playerUrl),uc,"A player recording timed out during background Shorts preparation."),u=Math.max(0,n.videoStartSeconds??0),h=DD(n,d.duration,a.video.duration),x=[{start:u,end:u+Math.max(.9,h),text:n.caption||n.title}];let g=u,S=performance.now(),k=0;const R=[];try{a.video.pause(),a.video.playbackRate=1,a.video.currentTime=Math.min(u,Math.max(0,(a.video.duration||u+1)-.05)),await lk(a.video),a.video.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&Ad(a.context,a.video,Bd,Wd,x,{format:"shorts",subtitles:!1}),await at(a.audioContext.resume(),Hm,"Background Shorts audio processing stopped responding."),ND(a.recorder),k=performance.now(),S=k,g=a.video.currentTime;const C=a.audioContext.currentTime+.08;R.push(rm(a.audioContext,a.audioMasterInput,d,C)),a.backingBuffer&&R.push(TP(a.audioContext,a.audioMasterInput,a.backingBuffer,C,u,h,Ra.current)),a.activeSources.push(...R),a.video.play().catch(()=>{}),await at(new Promise(U=>{const A=()=>{if(a.discarded||a.recorder.state==="inactive"){U();return}const F=performance.now(),$=(F-k)/1e3;if(Fk(a.video,u+$,F,{lastVideoTime:g,lastVideoAdvancedAt:S,update:(Q,J)=>{g=Q,S=J}}),a.video.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&Ad(a.context,a.video,Bd,Wd,x,{format:"shorts",subtitles:!1}),$<h){a.animationFrame=window.requestAnimationFrame(A);return}U()};A()}),Math.max(5e3,(h+5)*1e3),"Background Shorts frame rendering timed out."),a.discarded||(a.renderedDurationSeconds+=h)}finally{a.animationFrame&&(window.cancelAnimationFrame(a.animationFrame),a.animationFrame=void 0),Bk(a.recorder),a.video.pause(),R.forEach(C=>{try{C.stop()}catch{}C.disconnect()}),a.activeSources=a.activeSources.filter(C=>!R.includes(C))}}async function hP(a){let n=Or.current;if(n||(await Cs.current.catch(()=>{}),n=Or.current),!(!n||n.packId!==a.packId||n.discarded||n.closing)){Yl(),n.closing=!0;try{if(await Cs.current.catch(()=>{}),n.discarded||Or.current!==n||n.segmentKeys.size<a.completedCount)return;if(n.recorder.state!=="inactive"){try{n.recorder.requestData()}catch{}n.recorder.stop()}let s;try{s=await at(n.stopped,Jm,"Background Shorts recorder did not finish.")}catch(u){if(n.chunks.length===0)throw u;s=new Blob(n.chunks,{type:n.recorder.mimeType||"video/webm"})}const d=await at(hk(s,Math.max(.5,n.renderedDurationSeconds)),Rk,"Backgroun
156d Shorts metadata finalization timed out.").catch(()=>s);return zr.current[a.id]=d,d}catch(s){console.warn("Incremental Shorts finalize failed:",s);return}finally{Or.current===n&&(Or.current=void 0),Kl(),await _p(n)}}}async function gP(a,n){a.currentVideoUrl===n&&a.video.readyState>=HTMLMediaElement.HAVE_METADATA||(a.video.pause(),a.currentVideoUrl=n,a.video.src=n,a.video.load(),await at(Ha(a.video),uc,"Background Shorts source video timed out."))}async function wn(){const a=Or.current;Or.current=void 0,Cs.current=Promise.resolve(),Ur.current={},a&&await _p(a)}async function _p(a){if(a.discarded=!0,a.animationFrame&&(window.cancelAnimationFrame(a.animationFrame),a.animationFrame=void 0),a.activeSources.forEach(n=>{try{n.stop()}catch{}n.disconnect()}),a.activeSources=[],a.video.pause(),a.video.removeAttribute("src"),a.recorder.state!=="inactive")try{a.recorder.stop()}catch{}a.outputStream.getTracks().forEach(n=>n.stop()),a.unmountVideo(),a.recorderStarted&&await at(a.stopped,Jm,"Background Shorts recorder did not stop.").catch(()=>{}),await a.audioContext.close().catch(()=>{})}function Sb(){let a=!1;["dub","shorts"].forEach(n=>{yn.current[n]==="background"&&(Ue.current[n]?.abort(),a=!0)}),a&&(wo.current="",zt.current="",dr.current=!0)}async function xb(){const a=["dub","shorts"].flatMap(s=>{const d=Ue.current[s];return d&&yn.current[s]==="background"?[{format:s,controller:d}]:[]});return a.length===0||(Uo.current=!1,dr.current=!1,wo.current="",zt.current="",P("Stopping background video preparation before download."),a.forEach(({controller:s})=>s.abort()),(await Promise.all(a.map(({format:s,controller:d})=>fP(s,d,Pk)))).every(Boolean))?!0:(P("The previous video task could not stop. Refresh this result and try the download again."),!1)}function fP(a,n,s){const d=Date.now()+Math.max(1,s);return new Promise(u=>{const h=()=>{if(Ue.current[a]!==n){u(!0);return}if(Date.now()>=d){u(!1);return}window.setTimeout(h,50)};h()})}async function Rb(a){if(!Bt())return!0;const n=a==="dub"?"shorts":"dub",s=Ue.current[n];if(!s)return!0;if(yn.current[n]!=="background")return P("Another video download is already preparing. Please wait for it to finish."),!1;Uo.current=!0,n==="dub"?wo.current="":zt.current="",s.abort();for(let d=0;d<40;d+=1){if(Ue.current[n]!==s)return!0;await new Promise(u=>window.setTimeout(u,50))}return P("The previous video task is still stopping. Please try again in a moment."),!1}function Pb(a){!Bt()||document.hidden||!Uo.current||Ue.current.dub||Ue.current.shorts||window.setTimeout(()=>Ia(a),300)}function Yl(){jl.current+=1,Vc(!0)}function bP(a,n,s={}){const d=Ze.current,u=Le.find(x=>x.id===ye),h=wk();return{mode:T==="multi"?Ge(d?.game):"single",roomId:d?.roomId,packId:a.packId,packTitle:a.packTitle,packSource:u?.summary?.workshopId||a.workshopPackId?"workshop":"local",workshopPackId:a.workshopPackId,workshopPackSlug:a.workshopPackSlug,clipCount:a.clips.length,completedCount:a.completedCount,format:n,browser:navigator.userAgent.
156slice(0,120),platform:navigator.platform.slice(0,120),hardwareConcurrency:navigator.hardwareConcurrency,deviceMemoryGb:navigator.deviceMemory,deviceClass:CN(Bt(),h),browserFamily:MN(navigator.userAgent),...s}}function Hr(a,n,s,d={}){pe?.sessionId&&fd(a,pe.sessionId,bP(n,s,d))}function Cb(a,n){return n?.aborted?Promise.reject(pu()):(Ms.current?.cancel(),new Promise((s,d)=>{let u=!1;const h=S=>{u||(u=!0,n?.removeEventListener("abort",x),Ms.current===g&&(Ms.current=void 0,Z(!1)),S())},x=()=>h(()=>d(pu())),g={cancel:x,continuePlayback:()=>{a.play().then(()=>h(s),S=>h(()=>d(S)))}};Ms.current=g,n?.addEventListener("abort",x,{once:!0}),Z(!0)}))}function yP(a){const n=Ue.current[a];n&&(Fr.current[a]=!1,rt.current[a]=void 0,a==="dub"?wo.current="":zt.current="",n.abort(),P("Video preparation cancelled. Press download to try again."))}function Kl(){jl.current=Math.max(0,jl.current-1),jl.current===0&&Vc(!1)}function Mb(){if(Ri(),lr.current=!1,Me(!1),Bt()){dr.current=!1;return}if(!dr.current)return;dr.current=!1;const a=ro();a&&window.setTimeout(()=>Ia(a),300)}function Ia(a){const n=Bt();if(ui.current||!a.completedCount||!a.clips.some(u=>u.videoUrl))return;if(document.hidden){Uo.current=!0;return}if(lr.current){dr.current=!0;return}if(n&&(Ue.current.dub||Ue.current.shorts)){Uo.current=!0;return}Uo.current=!1;const s=()=>
156{if(ui.current||n||zt.current===a.id||zr.current[a.id]||li.current[a.id])return;const u=Ur.current[a.id];if(u){zt.current=a.id,u.then(h=>{!h&&!zr.current[a.id]&&!li.current[a.id]&&(delete Ur.current[a.id],zt.current="",s())});return}zt.current=a.id,Xp(a,!1,!1)};if(wo.current===a.id||Ca.current[a.id]){s();return}const d=Ps.current[a.id];if(d){d.then(s,s);return}wo.current=a.id,Zp(a,!1,!1).then(s,s)}async function Yp(a){const n=Ze.current;if(T!=="multi"||!n?.game||!Da(n.game))return;const s=`${n.roomId}:${n.game.startedAt}:${a.id}`;if(Pp.current!==s){Pp.current=s;try{const d=await IM({roomCode:n.roomCode,playerToken:n.playerToken});yi(n)&&Ne(d)}catch{Pp.current=""}}}function Jr(a,n,s,d=!1,u=!1){Yp(n);const h=yt(),x=s==="shorts"?"Save Shorts Video":"Save Landscape Video";if(bN(h,u)){P(`${s==="shorts"?"Shorts":"Landscape"} video is ready. Tap ${x} to choose where to save it.`);return}const g=$D(a,n,s,d);P(h?"Opening iPhone save options.":"Starting video download."),Hr("video_save_started",n,s,{stage:"saving",source:h?"native-share":"browser-download",fileSizeBytes:a.size}),$m(a,g,h).then(S=>{Hr(S==="cancelled"?"video_save_cancelled":"video_save_completed",n,s,{stage:"saving",source:h?"native-share":"browser-download",result:S,success:S!=="cancelled",fileSizeBytes:a.size}),P(S==="cancelled"?`Save cancelled. Tap ${x} to try again.`:S==="shared"?"Sent to the iPhone save menu. Check Photos or Files before leaving.":"Video download started.")}).catch(S=>{Hr("video_save_failed",n,s,{stage:"saving",source:h?"native-share":"browser-download",success:!1,fileSizeBytes:a.size,errorName:S instanceof Error?S.name:"UnknownError",errorMessage:S instanceof Error?S.message:"Unknown video save error"});const k="The video was created, but the browser could not save it. Your recordings are still saved.",R=Km(n,s,"finalizing",1,S);xr({...R,summary:k}),P(k)})}async function Kp(){if(!(document.hidden||fp.current)){if(Ue.current.dub||Ue.current.shorts){Qt.current&&window.clearTimeout(Qt.current),Qt.current=window.setTimeout(()=>{Qt.current=void 0,Kp()},300);return}fp.current=!0;try{const a=rt.current.dub;a&&await Zp(a.work,!0,a.includeSubtitles,a.compatibilityMode);const n=rt.current.shorts;if(n&&!document.hidden&&await Xp(n.work,!0,n.includeSubtitles,n.compatibilityMode),!document.hidden&&Uo.current){const s=ro();s&&Ia(s)}}finally{fp.current=!1}}}async function Zp(a,n=!1,s=!1,d=!1,u){const h=s?bn:Ca,x=s?Sl:Ps;if(n&&(rt.current.dub={work:a,includeSubtitles:s,compatibilityMode:d}),document.hidden){Uo.current=!0,n||(wo.current="");return}const g=h.current[a.id];if(g)return s||Yp(a),n&&(Jr(g,a,"dub",s),delete rt.current.dub),g;const S=x.current[a.id];if(S){if(n){P("Dub video is still preparing.");try{const $=await S;return Jr($,a,"dub",s),delete rt.current.dub,$}catch($){if(!qa($)||!Fr.current.dub)throw delete rt.current.dub,$;return}}return S}if(!await Rb("dub"))return;Yl(),Bn(!0),Wn("dub"),$n("loading"),na(1),Sr(n),sa(0),P(n?s?"Preparing dubbed video download.":"Preparing clean dubbed video download.":"Preparing dubbed video in the background.");const k=new AbortController;Fr.current.dub=!1,Ue.current.dub=k,yn.current.dub=n?"download":"background";const R=Date.now();let C="loading",U=1,A;n&&Hr("video_export_started",a,"dub",{stage:"loading",attempt:1});const F=jb(a,{format:"dub",onPlaybackBlocked:n&&yt()?Cb:void 0,onProgress:sa,onStage:($,Q=1)=>{C=$,U=Q,$n($),na(Q)},onProfile:$=>{A=$},signal:k.signal,subtitles:s,compatibilityMode:d,audioContext:u});x.current[a.id]=F;try{const $=await F;return h.current[a.id]=$,s||Yp(a),n?(Jr($,a,"dub",s),delete rt.current.dub,xr(void 0),Hr("video_export_completed",a,"dub",{stage:"finalizing",success:!0,durationMs:Date.now()-R,...A})):s||eR(a,$),n||P("Dubbed video ready."),$}catch($){if(qa($)){n&&!Fr.current.dub&&(delete rt.current.dub,P("Dubbed video download cancelled."));return}if(n&&delete rt.current.dub,Hr("video_export_failed",a,"dub",{stage:C,attempt:U,success:!1,durationMs:Date.now()-R,errorName:$ instanceof Error?$.name:"UnknownError",errorMessage:$ instanceof Error?$.message:"Unknown video export error",mediaErrorCode:$ instanceof Bi?$.mediaErrorCode:void 0,source:n?"download":"background-preparation",...A}),n){const Q=Km(a,"dub",C,U,$,A);xr(Q),P(Q.summary)}
156else P("Background video preparation paused. Tap download when you are ready.");return}finally{delete x.current[a.id],Ue.current.dub===k&&(Ue.current.dub=void 0,yn.current.dub=void 0),Bn(!1),Wn(void 0),Sr(!1),sa(0),Kl(),Pb(a)}}async function Xp(a,n=!1,s=!1,d=!1,u){const h=s?ci:zr,x=s?Rl:li;if(n&&(rt.current.shorts={work:a,includeSubtitles:s,compatibilityMode:d}),document.hidden){Uo.current=!0,n||(zt.current="");return}const g=h.current[a.id];if(g)return n&&(Jr(g,a,"shorts",s),delete rt.current.shorts),g;const S=x.current[a.id];if(S){if(n){P("Shorts video is still preparing.");try{const $=await S;return Jr($,a,"shorts",s),delete rt.current.shorts,$}catch($){if(!qa($)||!Fr.current.shorts)throw delete rt.current.shorts,$;return}}return S}if(!await Rb("shorts"))return;Yl(),Bn(!0),Wn("shorts"),$n("loading"),na(1),Sr(n),sa(0),P(n?s?"Preparing Shorts video download.":"Preparing clean Shorts video download.":"Preparing Shorts video in the background.");const k=new AbortController;Fr.current.shorts=!1,Ue.current.shorts=k,yn.current.shorts=n?"download":"background";const R=Date.now();let C="loading",U=1,A;n&&Hr("video_export_started",a,"shorts",{stage:"loading",attempt:1});const F=jb(a,{format:"shorts",onPlaybackBlocked:n&&yt()?Cb:void 0,onProgress:sa,onStage:($,Q=1)=>{C=$,U=Q,$n($),na(Q)},onProfile:$=>{A=$},signal:k.signal,subtitles:s,compatibilityMode:d,audioContext:u});x.current[a.id]=F;try{const $=await F;return h.current[a.id]=$,n&&(Jr($,a,"shorts",s),delete rt.current.shorts,xr(void 0),Hr("video_export_completed",a,"shorts",{stage:"finalizing",success:!0,durationMs:Date.now()-R,...A})),n||P("Shorts video ready."),$}catch($){if(qa($)){n&&!Fr.current.shorts&&(delete rt.current.shorts,P("Shorts video download cancelled."));return}if(n&&delete rt.current.shorts,Hr("video_export_failed",a,"shorts",{stage:C,attempt:U,success:!1,durationMs:Date.now()-R,errorName:$ instanceof Error?$.name:"UnknownError",errorMessage:$ instanceof Error?$.message:"Unknown video export error",mediaErrorCode:$ instanceof Bi?$.mediaErrorCode:void 0,source:n?"download":"background-preparation",...A}),n){const Q=Km(a,"shorts",C,U,$,A);xr(Q),P(Q.summary)}else P("Background Shorts preparation paused. Tap download when you are ready.");return}finally{delete x.current[a.id],Ue.current.shorts===k&&(Ue.current.shorts=void 0,yn.current.shorts=void 0),Bn(!1),Wn(void 0),Sr(!1),sa(0),Kl(),Pb(a)}}async function vP(a=ro()){if(!a){P("No completed audio track to download yet.");return}const n=`${_S(a.packTitle||"voxalike-dub")}-audio.wav`,s=Pl.current[a.id];if(s){P(yt()?"Opening iPhone save options.":"Starting audio download."),$m(s,n,yt()).then(d=>{P(d==="cancelled"?"Save cancelled. Tap Save Audio Track to try again.":d==="shared"?"Audio track sent to the iPhone save menu.":"Audio track download started.")}).catch(d=>{P(d instanceof Error?`Audio save failed: ${d.message}`:"Audio save failed. Try again.")});return}Yl(),P("Preparing audio track.");try{const d=await wP(a),u=En(d);Pl.current[a.id]=u,yt()?P("Audio track is ready. Tap Save Audio Track to choose where to save it."):($m(u,n,!1),P("Audio track download started."))}catch(d){P(d instanceof Error?`Audio download failed: ${d.message}`:"Audio download failed.")}finally{Kl()}}async function jb(a,n={}){let s;for(let d=1;d<=2;d+=1){zi(n.signal);try{return await kP(a,{...n,compatibilityMode:n.compatibilityMode===!0||Bt()&&d>1,onStage:(u,h=1)=>{n.onStage?.(u,Math.max(d,h))}})}catch(u){if(!MD(u)||d>=2)throw u;s=u,n.onProgress?.(0),await new Promise(h=>window.setTimeout(h,250))}}throw s??new Ah}async function kP(a,n){const s=n.format??"dub",d=n.onProgress,u=n.signal,h=n.subtitles===!0;zi(u);const x=a.clips.find(De=>De.videoUrl);if(!x?.videoUrl)throw new Error("This pack has no video file.");if(!("MediaRecorder"in window))throw new Error("This browser cannot export video.");const g=document.createElement("video");g.crossOrigin="anonymous",g.muted=!0,g.playsInline=!0,g.preload="auto",g.src=x.videoUrl;const S=Uk(g);n.onStage?.("loading",1);let k;try{k=await TI(g,{signal:u,onPlaybackBlocked:n.onPlaybackBlocked,onAttempt:De=>n.onStage?.("loading",De)})}catch(De){if(g.pause(),g.removeAttribute("src"),S(),yt()&&x.videoFile&&De instanceof Bi){const $o=await DI(x.videoFile).catch(()=>{});if($o?.av1||$o?.opus){const fr=new Error(`This mod's MP4 uses ${[$o.av1?"AV1 video":"",$o.opus?"Opus audio":""].filter(
156Boolean).join(" and ")}, which this iPhone could not play. The mod needs an H.264/AAC MP4 source.`);throw fr.name="ExportSourceCodecError",fr}}throw De}n.onStage?.("loading",k);const R=n.compatibilityMode===!0,C=wk(),U=R?qI():GI(C),{width:A,height:F}=QN(s,g.videoWidth||1280,g.videoHeight||720,C==="constrained",R),$=document.createElement("canvas");$.width=A,$.height=F;const Q=$.getContext("2d");if(!Q)throw g.pause(),g.removeAttribute("src"),S(),new Error("Canvas export is unavailable.");const J=n.audioContext??new AudioContext,le=!n.audioContext,de=J.createMediaStreamDestination(),ie=yk(J,de);let Pe,dt,Je,Fe=!1;const Ai=[],gr=[],Ys=[],Kr=[];let Ii=0,Ft=Number.isFinite(g.duration)&&g.duration>0?g.duration:0,Wo=0,za=0,Oa=0,ao,Zr=()=>{},Ks=!1,id=()=>{};const sd=new Promise((De,$o)=>{id=$o}),Zs=()=>{g.pause(),Je&&Je.state!=="inactive"&&Je.stop()};u?.addEventListener("abort",Zs,{once:!0});try{if(zi(u),await at(J.resume(),Hm,"Browser audio processing did not start for video export.",u),a.backingTrackUrl){const ue=await at($a(J,a.backingTrackUrl),uc,"Background audio timed out while preparing the video.",u).catch(re=>{if(qa(re))throw re});ue&&(gr.push({buffer:ue,offsetSeconds:0,gain:Ra.current}),Ft=Math.max(Ft,ue.duration))}for(const ue of a.clips){const re=a.takes[ue.id];if(!re?.playerAudio&&!re?.playerUrl)continue;const Mt=re.playerAudio?Ed(J,re.playerAudio):await at($a(J,re.playerUrl),uc,"A player recording timed out while preparing the video.",u),no=Math.max(0,ue.videoStartSeconds??0);gr.push({buffer:Mt,offsetSeconds:no,gain:1}),Ft=Math.max(Ft,no+Mt.duration),h&&Kr.push({start:no,end:Math.max(no+.9,ue.videoEndSeconds??no+Mt.duration),text:ue.caption||ue.title})}h&&LD(Kr,Number.isFinite(g.duration)?g.duration:void 0),Ft=Math.max(.5,Ft);const De=ED(g);Fe=!R&&_I({format:s,subtitles:h,supportsCaptureStream:!!De?.getVideoTracks().some(ue=>ue.readyState==="live"),sourceWidth:g.videoWidth||A,sourceHeight:g.videoHeight||F,outputWidth:A,outputHeight:F,sourceDurationSeconds:g.duration,timelineDurationSeconds:Ft}),Fe&&De?Pe=De:(De?.getTracks().forEach(ue=>ue.stop()),Pe=$.captureStream(U),Fe=!1),Pe.getVideoTracks().length||(Pe.getTracks().forEach(ue=>ue.stop()),Pe=$.captureStream(U),Fe=!1),dt=new MediaStream([...Pe.getVideoTracks(),...de.stream.getAudioTracks()]);const $o=Wk(yt());Je=$k(dt,$o);const fr=Je;n.onProfile?.({exportWidth:A,exportHeight:F,frameRate:U,renderPath:Fe?"direct-track":R?"canvas-compatibility":"canvas"});const cd=new Promise((ue,re)=>{fr.ondataavailable=Mt=>{Mt.data.size>0&&Ai.push(Mt.data)},fr.onerror=()=>re(new Error("Video recorder failed.")),fr.onstop=()=>ue(new Blob(Ai,{type:fr.mimeType||"video/webm"}))}),ld=()=>{const ue=performance.now(),re=Wo?(ue-Wo)/1e3:0;ao&&!g.seeking&&g.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&(FI(ao,g.currentTime,ue),BI(ao,AD(g),ue)),Wo&&Fk(g,re,ue,{lastVideoTime:za,lastVideoAdvancedAt:Oa,update:(no,jt)=>{za=no,Oa=jt}});const Mt=Math.min(re,Math.max(0,g.duration));if(ao&&!Ks&&WI(ao,Mt,g.duration,ue)){Ks=!0,id(new Ah);return}g.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA&&(Fe||Ad(Q,g,A,F,Kr,{format:s,subtitles:h}),Ft>0&&d?.(Math.min(1,re/Ft))),re<Ft&&Je?.state!=="inactive"&&(Ii=window.requestAnimationFrame(ld))};g.playbackRate=1,g.currentTime=0,await lk(g,u),yt()||Je.start(250);try{await at(Vk(g.play(),u),8e3,"Source video playback stalled while export was starting.",u)}catch(ue){if(!Eh(ue)||!n.onPlaybackBlocked)throw ue;await n.onPlaybackBlocked(g,u)}yt()&&Je.start(250),Wo=performance.now();const Xs=typeof g.requestVideoFrameCallback=="function";ao=OI(Wo,g.currentTime,Xs),Zr=ID(g,(ue,re)=>{ao&&UI(ao,ue,re)}),n.onStage?.("rendering",k),d?.(.001),za=g.currentTime,Oa=Wo;const Qs=J.currentTime+.08;gr.forEach(({buffer:ue,offsetSeconds:re,gain:Mt})=>{Ys.push(rm(J,ie,ue,Qs+re,0,Mt))}),ld(),await Vk(Promise.race([TD(Ft),sd]),u),zi(u),Fe||Ad(Q,g,A,F,Kr,{format:s,subtitles:h}),n.onStage?.("finalizing",k);try{Je.requestData()}catch{}Je.stop();let Mn;try{Mn=await at(cd,Jm,"Video recorder did not finish. Please retry the download.",u)}catch(ue){if(qa(ue)||Ai.length===0)throw ue;console.warn("Video recorder stop timed out;
156 using the flushed recording chunks.",ue),Mn=new Blob(Ai,{type:Je.mimeType||"video/webm"})}zi(u);try{return await at(hk(Mn,Ft),Rk,"Video metadata finalization timed out.",u)}catch(ue){if(qa(ue))throw ue;return console.warn("Video metadata finalization timed out; returning the playable recording.",ue),Mn}}finally{u?.removeEventListener("abort",Zs),Je&&Je.state!=="inactive"&&Je.stop(),Ii&&window.cancelAnimationFrame(Ii),Zr(),Ys.forEach(De=>{try{De.stop()}catch{}De.disconnect()}),dt?.getTracks().forEach(De=>De.stop()),Pe?.getTracks().forEach(De=>De.stop()),g.pause(),g.removeAttribute("src"),S(),le&&await J.close().catch(()=>{})}}async function wP(a){const n=await SP(a),s=xN,d=Math.max(1,Math.ceil(n*s)),u=new OfflineAudioContext(1,d,s),h=u.destination;if(a.backingTrackUrl){const g=new AudioContext,S=await $a(g,a.backingTrackUrl).catch(()=>{});if(await g.close().catch(()=>{}),S){const k=u.createBufferSource(),R=u.createGain();k.buffer=S,R.gain.value=Ra.current,k.connect(R),R.connect(h),k.start(0)}}for(const g of a.clips){const S=a.takes[g.id];if(!S?.playerAudio&&!S?.playerUrl)continue;const k=u.createBufferSource();k.buffer=S.playerAudio?Ed(u,S.playerAudio):await $a(u,S.playerUrl),k.connect(h),k.start(Math.max(0,g.videoStartSeconds??0))}const x=await u.startRendering();return{samples:PN(Oh(x).samples),sampleRate:s}}async function SP(a){const n=a.clips.find(s=>s.videoUrl);if(n?.videoUrl){const s=document.createElement("video");s.preload="metadata",s.src=n.videoUrl,await Ha(s);const d=s.duration;if(s.removeAttribute("src"),Number.isFinite(d)&&d>0)return d}return Math.max(1,...a.clips.map(s=>{const d=a.takes[s.id],u=d?.playerAudio?d.playerAudio.samples.length/d.playerAudio.sampleRate:0;return(s.videoStartSeconds??0)+u}))}function Fo(a){return a.dataset.roundId===ke.current?.data.roundId}function Qp(a,n){return ke.current?.data.roundId===a.data.roundId&&xo.current===n}async function Tb(a,n){const s=fe.current;if(!s||!a.referenceVideoUrl||!Fo(s)||(await Promise.race([Ha(s),Gd(1200)]),fe.current!==s||ke.current?.data.roundId!==a.data.roundId||n!==void 0&&xo.current!==n)||(Sn(s,a),await Promise.race([mc(s),Gd(1200)]),fe.current!==s||ke.current?.data.roundId!==a.data.roundId||n!==void 0&&xo.current!==n)||(await jD(s,1500),fe.current!==s||ke.current?.data.roundId!==a.data.roundId||n!==void 0&&xo.current!==n))return!1;s.muted=!0;try{await s.play(),await Ok(s)}catch{return!1}return fe.current!==s||ke.current?.data.roundId!==a.data.roundId||n!==void 0&&xo.current!==n?(s.pause(),!1):!0}function Sn(a=fe.current,n=ke.current){if(!a||!n?.referenceVideoUrl)return;const s=n.videoStartSeconds??0;try{a.playbackRate=1;const d=Number.isFinite(a.duration)?Math.max(0,a.duration-.1):s;a.currentTime=Math.min(s,d)}catch{a.playbackRate=1,a.currentTime=0}}function xP(a,n=0){_r(!0);const s=performance.now();sr.current=!0,Ef.current=s,Af.current=a,kl.current=Math.max(0,Math.min(a,n)),hp.current=0,ll(0),ot(0),bl.current=window.setInterval(()=>{const d=(performance.now()-s)/1e3,u=Math.min(a,d);ll(u),wf(vl.current)},32),fl.current=window.setTimeout(()=>{Jp("time-limit")},Math.max(250,a*1e3))}function _r(a){sr.current=!1,nP(),yl.current&&(window.clearTimeout(yl.current),yl.current=void 0),fl.current&&(window.clearTimeout(fl.current),fl.current=void 0),bl.current&&(window.clearInterval(bl.current),bl.current=void 0),a&&(kl.current=0,ll(0),ot(0),Sa(!1),nr(!1),Vl())}function Zl(){Pa.current!==void 0&&(window.cancelAnimationFrame(Pa.current),Pa.current=void 0)}function Eb(a){Zl();let n=0;const s=d=>{const u=a();if(u===void 0){Pa.current=void 0;return}(n===0||d-n>=32)&&(n=d,ot(Math.max(0,Math.min(1,u)))),Pa.current=window.requestAnimationFrame(s)};Pa.current=window.requestAnimationFrame(s)}function Xl(a){!Fo(a)||sr.current||a.tagName==="VIDEO"&&ke.current?.referenceAudioUrl||Eb(()=>{if(a.paused||a.ended||sr.current)return;const n=Si(),s=a.tagName==="VIDEO",d=s?Ql():em(a),u=s?ke.current?.videoStartSeconds??0:0,h=Math.max(0,Math.min(1,(a.currentTime-u)/d));return n.contentStartProgress+h*(n.contentEndProgress-n.contentStartProgress)})}function Ab(a){Fo(a)&&Eb(()=>{if(!(!Fo(a)||a.paused||a.ended))return Sd(a.currentTime,tm(a),Xr)})}function Ql(){const a=om(),n=PP();return Math.max(.5,a,n??0)}function Ws(){return Math.max(.5,om()+qm)}function Ib(){return Math.max(.5,Ws()+Xr)}function Si(){return LA(Math.max(.001,Ws()-qm),Xr,qm)}function em(a){const n=Number.isFinite(a?.duration)&&a&&a.duration>0?a.duration:0;return Math.max(.5,n||om())}function tm(a){const n=Number.isFinite(a?.duration)&&a&&a.duration>0?a.duration:0,s=Ut(),d=Na(s),u=d?je.current[d]:void 0,h=u?.playerAudio?u.playerAudio.samples.length/u.playerAudio.sampleRate:0;return Math.max(.5,n||h||Oe?.duration||Ws())}function Nb(a){if(!Fo(a))return;const n=tm(a);ot(Sd(a.currentTime,n,Xr))}function Db(a){!Fo(a.currentTarget)||ir.current!==a.currentTarget||(fe.current?.pause(),RP())}function RP(){Zl(),ht.current?.pause(),nr(!1),ot(1)}function xi(a,n){return n?t.jsxs("span",{className:"stage-playback-indicator","aria-hidden":"true",children:[t.jsx("i",{}),t.jsx("i",{}),t.jsx("i",{})]}):t.jsxs(t.Fragment,{children:[t.jsx(_e,{size:18}),r(a)]})}
156function Lb(a){const n=!!H?.backingTrackUrl;return t.jsxs("div",{ref:Pf,className:`my-take-preview-control${fs?" mix-open":""}`,children:[t.jsx("button",{className:`ghost-button${ks?" is-playing":""}`,type:"button",disabled:a,onClick:()=>{QR()},"aria-label":ks?"Recorded take with background audio, playing":void 0,"aria-pressed":ks,title:r(n?"Play your recorded take with background audio":"Play your recorded take"),children:xi("My Take",ks)}),n?t.jsxs(t.Fragment,{children:[t.jsx("button",{className:"my-take-mix-button",type:"button","aria-label":"Adjust My Take background volume","aria-expanded":fs,onClick:()=>il(s=>!s),title:r("Background volume"),children:t.jsx(dw,{size:13})}),fs?t.jsx("div",{className:"my-take-mix-popover",role:"group","aria-label":"My Take background volume",children:t.jsxs("label",{children:[t.jsx("span",{children:r("Background")}),t.jsxs("strong",{children:[Math.round(hn*100),"%"]}),t.jsx("input",{type:"range",min:"0",max:"1",step:"0.05",value:hn,onChange:s=>_R(Number(s.target.value)),"aria-label":"Background volume"})]})}):null]}):null]})}function om(){const a=q?.referenceAudio?q.referenceAudio.samples.length/q.referenceAudio.sampleRate:0;return Math.max(q?.referenceFeatures?.duration??0,q?.data.clip.duration??0,a)}function PP(){const a=Ut();if(!a||a.videoStartSeconds===void 0)return;if(a.videoEndSeconds!==void 0&&a.videoEndSeconds>a.videoStartSeconds)return a.videoEndSeconds-a.videoStartSeconds;const n=H?.clips??[],s=n.findIndex(h=>h.id===a.id);if(s<0)return;const d=a.metadata.videoPath||a.videoUrl||"",u=n.slice(s+1).find(h=>{const x=h.metadata.videoPath||h.videoUrl||"";return h.videoStartSeconds!==void 0&&h.videoStartSeconds>a.videoStartSeconds&&x===d});return u?.videoStartSeconds!==void 0?u.videoStartSeconds-a.videoStartSeconds:void 0}function Vb(a){return new Date(a.data.expiresAt).getTime()<=Date.now()+1e4}function CP(a,n){return Vb(a)||a.sessionId!==n.sessionId}function MP(a){return a instanceof Error&&a.message.includes("Round token does not match")}function jP(a,n,s,d=0,u=1){const h=rm(a,a.destination,n,s,d,u);gp.current.push(h)}function rm(a,n,s,d,u=0,h=1){const x=a.createBufferSource();return x.buffer=s,zb(a,x,n,h),x.start(d,Math.max(0,Math.min(u,Math.max(0,s.duration-.01)))),x}function TP(a,n,s,d,u=0,h,x=1){const g=a.createBufferSource(),S=Math.max(0,Math.min(u,Math.max(0,s.duration-.01))),k=h!==void 0?Math.max(.05,Math.min(h,Math.max(.05,s.duration-S))):void 0;return g.buffer=s,zb(a,g,n,x),k!==void 0?g.start(d,S,k):g.start(d,S),g}function zb(a,n,s,d){const u=Zd(d);if(u===1){n.connect(s);return}const h=a.createGain();h.gain.value=u,n.connect(h),h.connect(s)}function Ri(){If.current.forEach(a=>window.clearTimeout(a)),If.current=[],ht.current?.pause(),zo.current?.pause(),Nf.current.forEach(a=>a.pause()),Nf.current=[],gp.current.forEach(a=>{try{a.stop()}catch{}a.disconnect()}),gp.current=[],wl.current&&(wl.current.close().catch(()=>{}),wl.current=null)}function Ob(a){Object.values(a).forEach(n=>{n.playerUrl&&URL.revokeObjectURL(n.playerUrl)})}function Ut(){return H?.clips[xt]}function $s(a,n){return`room:${a}:${n}`}function Na(a=Ut()){return yp.current||a?.id}function Ge(a){return a?.mode==="pvp"?"pvp":"coop"}function Ub(a){return a?.mode==="pvp"&&a.pvpFormat==="parallel"?"parallel":"turns"}function mr(a){return Ge(a)==="pvp"&&Ub(a)==="parallel"}function EP(a){return a==="pvp"?"PVP":"CO-OP"}function Pi(a){return a?.mode==="coop"&&(a.assignmentMode==="claim"||a.assignmentMode==="role")?a.assignmentMode:"round-robin"}function AP(a){return Ge(a)==="pvp"?mr(a)?"PVP RACE":"PVP TURNS":Pi(a)==="claim"?"CO-OP CLAIM":Pi(a)==="role"?"CO-OP ROLES":"CO-OP"}function am(a){return!!a?.allowRetakes}function Da(a){if(!a)return!1;if(a.completedAt||a.currentAssignmentIndex>=a.assignments.length)return!0;if(!mr(a))return!1;const n=new Set(a.withdrawnPlayerIds??[]);return a.assignments.every((s,d)=>n.has(s.playerId??"")||a.takes.some(u=>u.assignmentIndex===d))}function xn(a,n){const s=Array.from({length:n},(h,x)=>x);if(!a?.length)return s;const d=new Set,u=[];for(const h of a){const x=Math.round(h);x<0||x>=n||d.has(x)||(d.add(x),u.push(x))}return u.length?u:s}function La(a=H?.clips.length??0){return ff.length?xn(ff,a):xn(void 0,a)}function Fb(){const a=H?.clips??[];return La(a.length).map(n=>a[n]).filter(n=>!!n)}function Bb(){const a=La().indexOf(xt);return a>=0?a:xt}function IP(){return La().length}function ro(){if(sn){const n=ys.find(s=>DS(s,sn.game));if(n)return n}const a=Gs();return a||ys.find(n=>n.packId===H?.id)}function Gs(a=v,n=H){const s=a?.game;if(!a||!s||!n?.clips.length||!s.completedAt&&s.currentAssignmentIndex<s.assignments.length)return;const d=Ge(s),u=s.assignments.map((k,R)=>({assignment:k,assignmentIndex:R})).filter(({assignment:k})=>d==="coop"||k.playerId===a.playerId),h=[],x={},g={};for(const{assignment:k,assignmentIndex:R}of u){const C=$p(n,k);if(!C)continue;h.push(C);const U=s.takes.find(Q=>Q.assignmentIndex===R);if(!U){HA(s,R)&&(x[C.id]={clipId:C.id,playerUrl:C.audioUrl,keptOriginal:!0});continue}const A=$s(U.assignmentIndex,U.clipId),F=je.current[A]??je.current[C.id],$=U.scoreTotal!==void 0?ow(U):F?.score;x[C.id]={clipId:C.id,playerAudio:F?.playerAudio,playerUrl:F?.playerUrl??U.audioDataUrl,score:$},g[C.id]={clipId:C.id,playerUrl:F?.playerUrl??U.audioDataUrl,playerAudio:F?.playerAudio,score:$}}const S=Object.keys(x).length;if(S)return{id:`room-${a.roomCode}-${d}-${d==="pvp"?a.playerId:"coop"}-${s.startedAt}
156-${s.completedAt??"complete"}`,packId:s.packId,packTitle:s.packTitle,workshopPackId:s.workshopPackId,workshopPackSlug:s.workshopPackSlug,createdAt:s.completedAt&&Date.parse(s.completedAt)||Date.now(),clips:h,takes:x,score:Nd(g,h),completedCount:S,backingTrackUrl:n.backingTrackUrl}}async function hr(a){const n=++gt.current;Jc.current=void 0,tr([]),P(`Loading ${sm(a)}.`);const s=Fm(a);if(!s.length){Jc.current=new Error("Saved dub mod has no files."),P("Saved dub mod has no files. Import it again.");return}try{const d=a.kind==="zip"?await bc(s[0]):await Em(s);if(n!==gt.current){d.release();return}const u={...a,summary:{...Bo(d),workshopId:a.summary?.workshopId,workshopSlug:a.summary?.workshopSlug,workshopPackageHash:a.summary?.workshopPackageHash,workshopUpdatedAt:a.summary?.workshopUpdatedAt}};return await ak(u).catch(()=>{}),nn(u.id),ga(u.id),Hs(u),Mi(d,Yr(d,"Loaded from library.")),d}catch(d){Jc.current=d,n===gt.current&&P(d instanceof Error?d.message:"Saved dub mod could not be loaded.");return}}async function Wb(a){try{const n=Yt.find(u=>Th(u,a)),s=n?sk(n,Le):[a];await nm(s);const d=Math.max(0,s.length-1);P(d?`${sm(a)} and ${d} duplicate ${d===1?"copy":"copies"} removed from local library.`:`${sm(a)} removed from local library.`)}catch(n){const s=n instanceof Error?n.message:"Browser storage could not remove this dub mod.";P(`Could not remove this dub mod from browser storage: ${s}`)}}async function $b(a){await nm([a])}async function nm(a){const n=new Set(a.map(d=>d.id));await QA(n);const s=Le.filter(d=>!n.has(d.id));es(d=>d.filter(u=>!n.has(u.id))),ye&&n.has(ye)&&(Ro(),Qi(void 0),tr([]),nn(void 0)),Tr&&n.has(Tr)&&ga(s[0]?.id)}async function NP(){Uc(!0),Re("");try{const[a,n,s]=await Promise.all([Oy({force:!0}),Uy({force:!0}),ee?km({force:!0}):Promise.resolve(void 0)]);Pr(a),en(n),s&&ee&&(Do(s.packs),Cr(s.judgeMods),ii.current=ee.id,Rs.current=Date.now()),Re(a.length||n.length?"Workshop refreshed.":"No workshop mods uploaded yet.")}catch(a){Re(a instanceof Error?a.message:"Workshop unavailable.")}finally{Uc(!1)}}function DP(){!ee||Ve||Fc||Mr||(tr([]),xu(!0),Re("Refreshing Mods..."),pp.current=Date.now(),Ru(a=>a+1))}function Gb(){const a=window.scrollX,n=window.scrollY,s=Array.from(document.querySelectorAll(".workshop-list, .saved-pack-list, .mod-library-body")).map(d=>({element:d,left:d.scrollLeft,top:d.scrollTop}));return()=>{window.requestAnimationFrame(()=>{window.scrollTo(a,n),s.forEach(({element:d,left:u,top:h})=>{d.scrollLeft=u,d.scrollTop=h})})}}function qb(a,n){const s=hl.current.get(a);if(s)return s;wg(h=>new Set(h).add(a));const u=Tf.current.catch(()=>{}).then(n).finally(()=>{hl.current.get(a)===u&&hl.current.delete(a),wg(h=>{const x=new Set(h);return x.delete(a),x})});return hl.current.set(a,u),Tf.current=u.then(()=>{},()=>{}),u}function qs(a,n){const s=`${a}:${n}`;return Array.from(kg).some(d=>d===s||d.startsWith(`${s}:`))}function ed(a,n){const s=kg.values().next().value,d=`${a}:${n}`;return typeof s=="string"&&(s===d||s.startsWith(`${d}:`))}async function Hb(a,n){if(!ee){ft();return}if(Vr.current.has(a.id))return;const s=Gb(),d=new Set(Vr.current);d.add(a.id),Vr.current=d,qc(d),Re(n?`Subscribing to ${a.title}.`:`Unsubscribing from ${a.title}.`);const u={...a,subscribed:n},h=pt.findIndex(S=>S.id===a.id);let x=!1,g;n?(Pr(S=>S.map(k=>k.id===a.id?u:k)),Do(S=>S.some(k=>k.id===a.id)?S:[u,...S]),s()):(Pr(S=>S.map(k=>k.id===a.id?u:k)),Do(S=>S.filter(k=>k.id!==a.id)),s());try{if(n){const S=Fy({packId:a.id});g=_b(u,{},S),g.catch(()=>{}),await S}else await ZM({packId:a.id});if(x=!0,n)Pr(k=>k.map(R=>R.id===a.id?u:R)),Do(k=>k.some(R=>R.id===a.id)?k:[u,...k]),s(),await g&&(Re(`${a.title} subscribed and installed.`),P(`${a.title} subscribed and installed.`));else{const S=Kb(a);S.length&&await nm(S),Re(`${a.title} unsubscribed and removed from installed mods.`),P(`${a.title} unsubscribed and removed.`)}}catch(S){n&&!x?(Pr(R=>R.map(C=>C.id===a.id?{...C,subscribed:!1}:C)),Do(R=>R.filter(C=>C.id!==a.id)),s()):!n&&!x&&(Pr(R=>R.map(C=>C.id===a.id?{...C,subscribed:!0}:C)),Do(R=>{if(R.some(A=>A.id===a.id))return R;const C=[...R],U=h<0?C.length:Math.min(h,C.length);return C.splice(U,0,{...a,subscribed:!0}),C}),s());const k=!n&&x?`${a.title} unsubscribed, but its installed copy could not be removed.`:S instanceof Error?S.message:"Subscription update failed.";Re(k),P(k)}finally{const S=new Set(Vr.current);S.delete(a.id),Vr.current=S,qc(S)}}async function Jb(a,n){if(!ee){ft();return}if(xs.current.has(a.id))return;const s=Gb(),d=new Set(xs.current);d.add(a.id),xs.current=d,vg(d),Re(n?`Subscribing to ${a.title}.`:`Unsubscribing from ${a.title}.`);const u={...a,subscribed:n},h=tn.findIndex(S=>S.id===a.id);let x=!1,g;n?(en(S=>S.map(k=>k.id===a.id?u:k)),Cr(S=>S.some(k=>k.id===a.id)?S:[u,...S]),s()):(en(S=>S.map(k=>k.id===a.id?u:k)),Cr(S=>S.filter(k=>k.id!==a.id)),s());try{if(n){const S=XM({judgeModId:a.id});g=Yb(u,{},S),g.catch(()=>{}),await S}else await QM({judgeModId:a.id});if(x=!0,n)en(k=>k.map(R=>R.id===a.id?u:R)),Cr(k=>k.some(R=>R.id===a.id)?k:[u,...k]),s(),await g&&(Re(`${a.title} subscribed and installed. Enable it from Mods when ready.`),P(`${a.title} subscribed and installed.`));else{const S=Pn(a);S&&await od(S),Re(`${a.title} unsubscribed and removed from installed mods.`),P(`${a.title} unsubscribed and removed.`)}}catch(S){n&&!x?(en(R=>R.map(C=>C.id===a.id?{...C,subscribed:!1}:C)),Cr(R=>R.filter(C=>C.id!==a.id)),s()):!n&&!x&&(en(R=>R.map(C=>C.id===a.id?{...C,subscribed:!0}:C)),Cr(R=>{if(R.some(A=>A.id===a.id))return R;const C=[...R],U=h<0?C.length:Math.min(h,C.length);return C.splice(U,0,{...a,subscribed:!0}),C}),s());const k=!n&&x?`${a.title} unsubscribed, but its installed copy could not be removed.`:S instanceof Error?S.message:"Judge Mod subscription update failed.";Re(k),P(k)}finally{const S=new Set(xs.current);S.delete(a.id),xs.current=S,vg(S)}}async function LP(a,n){if(!ee)return;const s=Di(a);if(!s)return;const d=Li(s);
156if(xg!==d){Cg(""),Rg(d);try{const u=await ej({packId:s.id,slug:s.slug,score:n});XS(h=>({...h,[d]:u.userRating})),ex(h=>({...h,[d]:{ratingCount:u.ratingCount,ratingAverage:u.ratingAverage}})),VP(s,u.userRating,u.ratingCount,u.ratingAverage),P(`Rated ${a.packTitle} ${u.userRating}/5.`)}catch(u){const h=u instanceof Error?u.message:"Rating failed.";Cg(h),P(h)}finally{Rg(void 0)}}}function VP(a,n,s,d){const u=h=>Yk(h,a)?{...h,userRating:n,ratingCount:s,ratingAverage:d}:h;Pr(h=>h.map(u)),Do(h=>h.map(u))}function Ci(a,n={}){return _b(a,n)}function _b(a,n={},s=Promise.resolve()){const d=a.packageHash||a.updatedAt;return qb(`dub:${a.id}:${d}`,async()=>(await s,zP(a,n)))}async function zP(a,n){const s=ji(a);if(s&&!n.force)return nn(s.id),ga(s.id),Re(`${a.title} is already installed.`),P(`${a.title} is already installed.`),{pack:await hr(s),source:s};const d=!!(s&&ye===s.id),u=++Oo.current;gg(a.id),qn({packId:a.id,percent:0}),Re(`Installing ${a.title}.`);try{const h=await fetch(a.downloadUrl);if(!h.ok)throw new Error("Workshop download failed. Refresh and try again.");const x=await Gk(h,a.packageSize,C=>{u===Oo.current&&qn({packId:a.id,percent:C})}),g=new File([x],`${a.slug||a.id}.zip`,{lastModified:Date.now(),type:"application/zip"}),S=await bc(g);if(u!==Oo.current){S.release();return}const k=n.activate!==!1||d,R=await im("zip",[g],S,a,k);if(u===Oo.current){if(R)return s&&n.force&&s.id!==R.id&&await $b(s),Hs(R),k?(Mi(S,Yr(S,"Installed from Workshop.")),nn(R.id),ga(R.id)):S.release(),Re(`${S.title} installed in Mods.`),{pack:k?S:void 0,source:R};if(k)return Mi(S,Yr(S,"Installed from Workshop.")),Re(`${S.title} loaded, but browser storage could not save it.`),{pack:S};S.release(),Re(`${S.title} could not be saved to browser storage.`);return}}catch(h){if(u===Oo.current){const x=h instanceof Error?h.message:"Workshop install failed.";Re(x),P(x)}}finally{u===Oo.current&&(gg(void 0),qn({}))}}function td(a,n={}){return Yb(a,n)}function Yb(a,n={},s=Promise.resolve()){const d=a.packageHash||a.updatedAt;return qb(`judge:${a.id}:${d}`,async()=>(await s,OP(a,n)))}async function OP(a,n){const s=Pn(a);if(s&&!n.force)return Re(`${a.title} is already installed.`),P(`${a.title} is already installed.`),!0;const d=!!(s&&an.current?.id===s.id),u=++Oo.current;yg(a.id),qn({packId:a.id,percent:0}),Re(`Installing ${a.title}.`);try{const h=await fetch(a.downloadUrl);if(!h.ok)throw new Error("Judge Mod download failed. Refresh and try again.");const x=await Gk(h,a.packageSize,k=>{u===Oo.current&&qn({packId:a.id,percent:k})}),g=new File([x],`${a.slug||a.id}.zip`,{lastModified:Date.now(),type:"application/zip"}),S=await Kv(g,{enable:d,workshopId:a.id,workshopSlug:a.slug});return u!==Oo.current?(Ba(S),!1):(s&&n.force&&s.id!==S.id&&await od(s),await _s(),d?Ei(S):Ba(S),Re(`${S.title} installed in Mods. Enable it when ready.`),P(`${S.title} installed in Mods.`),!0)}catch(h){if(u===Oo.current){const x=h instanceof Error?h.message:"Judge Mod install failed.";Re(x),P(x)}}finally{u===Oo.current&&(yg(void 0),qn({}))}return!1}async function UP(){if(!(!St?.workshopPack||!v)){ep(!0);try{if(!await ob(v.game??{},St.workshopPack))return;const n=await Ci({...St.workshopPack,subscribed:!0});So.current="",bo(void 0),v&&await pr(v,n?.pack)}finally{ep(!1)}}}function FP(){bo(void 0)}async function BP(a){if(!a)return;const n=++gt.current;Hn(void 0),P("Reading local ZIP.");try{const s=await bc(a);if(n!==gt.current){s.release();return}Mi(s),P("Saving dub mod for the next refresh.");const d=await im("zip",[a],s);n===gt.current&&(d&&(nn(d.id),ga(d.id),Hs(d)),P(d?Yr(s,"Saved for refresh."):Yr(s,"Loaded, but browser storage could not save it.")))}catch(s){if(n===gt.current){const d=s instanceof Error?s.message:"ZIP import failed.";P(d),s instanceof vh&&Hn(d)}}}async function WP(a){if(!a?.length)return;const n=++gt.current,s=Array.from(a);Hn(void 0),P("Reading local folder.");try{const d=await Em(s);if(n!==gt.current){d.release();return}Mi(d),P("Saving dub mod for the next refresh.");const u=await im("folder",s,d);n===gt.current&&(u&&(nn(u.id),ga(u.id),Hs(u)),P(u?Yr(d,"Saved for refresh."):Yr(d,"Loaded, but browser storage could not save it.")))}catch(d){if(n===gt.current){const u=d instanceof Error?d.message:"Folder import failed.";P(u),d instanceof vh&&Hn(u)}}}async function im(a,n,s,d,u=!0){try{return await KA(a,eI(a,n),n,Bo(s,d),{makeCurrent:u})}catch{return}}function Mi(a,n=Yr(a)){Ro(),Il.current.clear(),kp.current.clear(),gn([]),bn.current={},Sl.current={},Ca.current={},Ps.current={},ci.current={},Rl.current={},zr.current={},li.current={},wo.current="",zt.current="",Ur.current={}
156,Qi(a),tr(Wy(a.warnings)),P(n)}function Yr(a,n){return a.warnings[0]?a.warnings[0]:`${a.clips.length} local clips ready.${n?` ${n}`:""}`}function Hs(a){es(n=>{const d=[a,...n.filter(u=>u.id!==a.id)].sort((u,h)=>u.id===a.id?-1:h.id===a.id?1:h.savedAt-u.savedAt);return Xm(d)})}function Bo(a,n){return{title:Wi(a.title,a.id),creator:a.creator,characters:a.characters,language:a.language,tags:a.tags,clipCount:a.clips.length,videoCount:Id(a.clips),videoDurationSeconds:Qm(a.clips),workshopId:n?.id,workshopSlug:n?.slug,workshopPackageHash:n?.packageHash,workshopUpdatedAt:n?.updatedAt}}function sm(a){return a.summary?.title||a.name}function ji(a){return Kb(a)[0]}function Kb(a){return sk(a,Le)}function $P(a){return Le.find(n=>Fd(a,n))}function GP(a){return!!ji(a)}function cm(a){return!!(a.summary?.workshopId||a.summary?.workshopSlug)}function Ti(a){const n=s=>da.has(s)&&!pt.some(d=>d.id===s);return a.summary?.workshopId&&n(a.summary.workshopId)?!0:Yt.some(s=>n(s.id)&&Fd(s,a))}function Rn(){return Le.filter(a=>!Ti(a))}function Va(){const a=Le.find(n=>n.id===ye);return a&&Ti(a)?void 0:H}function Pn(a){return Zb(a)||pa.find(n=>a.packageHash&&n.packageHash===a.packageHash?!0:n.id===a.manifestId||n.id===a.slug)}function Zb(a){return pa.find(n=>Zm(a,n))}function qP(a){return!!Pn(a)}function HP(a){return a.workshopId||a.workshopSlug?!0:tn.some(n=>Pn(n)?.id===a.id)}function Xb(a){return a.workshopId&&os.has(a.workshopId)?!0:ts.some(n=>os.has(n.id)&&Zm(n,a))}async function lm(a){ha(!0),Kt(""),tr([]);try{const n=await gA(a.id);Ei(n),await _s(),P(`${n.title} enabled.`),Kt(`${n.title} enabled.`)}catch(n){const s=n instanceof Error?n.message:"Judge Mod could not be enabled.";P(s),Kt(s)}finally{ha(!1)}}async function Qb(a){ha(!0),Kt(""),tr([]);try{const n=await od(a);P(`${a.title} removed from local library.`),Kt(n?"Default Voxalike judges enabled.":`${a.title} removed.`)}catch(n){const s=n instanceof Error?n.message:"Judge Mod could not be deleted.";P(s),Kt(s)}finally{ha(!1)}}async function od(a){const n=an.current?.id===a.id;return await bA(a.id),n&&Ei(void 0),await _s(),n}function ey(a){for(const n of Et){const s=a.images[n.key];if(s)return s}}function ty(a){const n=Et.filter(h=>!!a.images[h.key]).length,s=Et.reduce((h,x)=>h+Object.keys(a.sounds?.[x.key]??{}).length,0),d=Et.reduce((h,x)=>h+Object.keys(a.feedback?.[x.key]??{}).length,0);return[a.author?`By ${a.author}`:"",n?`${n} images`:"",s?`${s} sounds`:"",d?`${d} lines`:"",a.version?`v${a.version}`:""].filter(Boolean).join(" / ")||"Saved locally"}async function JP(a){if(rn.current.has(a.id))return rn.current.get(a.id);try{const n=Fm(a);if(!n.length)return;const s=a.kind==="zip"?await YT(n[0]):await KT(n);if(!s){console.log("No icon file found in pack:",a.name);return}const d=URL.createObjectURL(s);return rn.current.set(a.id,d),dx(u=>u+1),console.log("Loaded icon for pack:",a.name,"url:",d),d}catch(n){console.error("Failed to load pack icon:",n);return}}function Cn(){_r(!0),Pt.current?.pause(),ir.current?.pause(),zo.current?.pause(),ht.current?.pause(),fe.current?.pause(),ot(0),Sa(!1),nr(!1),il(!1),Rr(void 0),_t(void 0),la(void 0)}function rd(){const a=Ut(),n=Na(a);YR(),Cn(),vo(!0),n&&(Te.current&&x0(Te.current.id,n).catch(()=>{}
156),Vo(s=>{const d=s[n];d?.playerUrl&&URL.revokeObjectURL(d.playerUrl);const u={...s};return delete u[n],je.current=u,u}))}function dm(){if(T!=="multi"||!v?.game||!tt||ge)return!0;const a=Ut(),n=Na(a);return!!(a&&n&&Aa(v)&&!ko[n]?.playerUrl)}function _P(){if(T!=="multi"||!v?.game||!tt||ge||ae||ve)return!1;const a=Ut(),n=Na(a);return!!(a&&n&&Aa(v)&&am(v.game)&&ko[n]?.playerUrl)}const oy=!!(q&&!ae&&!ut&&dm()),ry=r(ut?"Connecting Mic...":ta?"Retry Mic":"Record"),Js=!!(v&&v.hostPlayerId===v.playerId);if(T==="preview")return uC();if(T==="menu")return t.jsxs("main",{className:"menu-shell",children:[t.jsx(Co,{active:"PLAY",authLoading:Ve,user:ee,onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsx("div",{className:"menu-home-scroll",children:t.jsxs("section",{className:"menu-playfield",children:[t.jsxs("div",{className:"menu-starfield","aria-hidden":"true",children:[t.jsx("i",{className:"menu-star menu-star-1"}),t.jsx("i",{className:"menu-star menu-star-2"}),t.jsx("i",{className:"menu-star menu-star-3"}),t.jsx("i",{className:"menu-star menu-star-4"}),t.jsx("i",{className:"menu-star menu-star-5"}),t.jsx("i",{className:"menu-star menu-star-6"}),t.jsx("i",{className:"menu-star menu-star-7"})]}),t.jsx("div",{className:"menu-stage",children:t.jsxs("div",{className:"menu-actions","aria-label":r("Main menu"),children:[t.jsxs("button",{className:"menu-button",type:"button",onClick:Lf,onMouseEnter:Ta,children:[t.jsx(Ry,{size:30}),t.jsx("span",{children:r("Single Player")})]}),t.jsxs("button",{className:"menu-button",type:"button",onClick:()=>Y("multi"),onMouseEnter:Ta,children:[t.jsx(In,{size:30}),t.jsx("span",{children:r("Multiplayer")})]}),t.jsxs("button",{className:"menu-button",type:"button",onClick:()=>Y("mods"),onMouseEnter:Ta,children:[t.jsx(bt,{size:30}),t.jsx("span",{children:r("Mods")})]}),t.jsxs("button",{className:"menu-button",type:"button",onClick:()=>Y("settings"),onMouseEnter:Ta,children:[t.jsx(Py,{size:30}),t.jsx("span",{children:r("Settings")})]}),We&&!vt?t.jsxs("button",{className:"menu-button pwa-install-button",type:"button",onClick:()=>{$x()},onMouseEnter:Ta,children:[t.jsx(uw,{size:30}),t.jsx("span",{children:r("Install App")})]}):null]})}),X?t.jsxs("aside",{className:"menu-contributor-notice","aria-label":r("Community Contributor applications"),children:[t.jsx("button",{className:"menu-contributor-notice-close",type:"button",onClick:()=>{sessionStorage.setItem(jk,"1"),me(!1)},title:r("Dismiss"),"aria-label":r("Dismiss contributor notice"),children:t.jsx(Tt,{size:15})}),t.jsx("span",{children:r("Limited Alpha Event")}),t.jsx("strong",{children:r("Community Contributor applications are open")}),t.jsx("p",{children:r("Earn Ad-Free access and an exclusive multiplayer badge.")}),t.jsxs("div",{children:[t.jsx("small",{children:r("Ends Sep 8, 2026 at 11:59 PM PT")}),t.jsx("a",{href:"/contributor",children:r("View details")})]})]}):null]})}),t.jsx(qo,{}),t.jsx(sM,{authLoading:Ve,adFree:!!ee?.adFree})]});if(T==="news"){const a=Ph.items.map(n=>tA(e,n));return t.jsxs("main",{className:"app-shell compact-shell",children:[t.jsx(Co,{active:"NEWS",authLoading:Ve,user:ee,onMenu:()=>gi("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar news-toolbar",children:[t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:r("DEVELOPER NEWS")}),t.jsx("h1",{children:r("Voxalike News")})]}),t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),t.jsx("section",{className:"panel news-layout",children:a.map((n,s)=>t.jsxs("article",{className:`news-card ${s===0?"featured-news-card":""}`,children:[t.jsx("div",{className:"news-date",children:n.date}),t.jsxs("div",{className:"news-content",children:[t.jsx("span",{children:n.label}),t.jsx("h2",{children:n.title}),n.changes?.length?t.jsx("ul",{className:"news-change-list",children:n.changes.map(d=>t.jsx("li",{children:d},d))}):n.body?t.jsx("p",{children:n.body}):null,n.emphasis?t.jsx("p",{className:"news-emphasis",children:t.jsx("strong",{children:n.emphasis})}):null,n.links?.length?t.jsx("p",{className:"news-link-row",children:n.links.map(d=>t.jsx("a",{href:d.href,...d.href.startsWith("/")?{}:{target:"_blank",rel:"nofollow noopener noreferrer"},children:d.label},d.href))}):null]})]},`${n.date}-${n.title}`))}),t.jsx(qo,{})]})}if(T==="guide"){const a=O==="/docs"||O.startsWith("/docs/"),n=a?Mc:qi,s=a?Mc.sections:JE,d=a?Rh:xh,u=a?"/docs":"/guide",h=FS(O),x=gw(O,window.location.search),g=h.sectionId?s.find(A=>A.id===h.sectionId):void 0,S=A=>d.find(F=>F.sectionId===A.id)?.path??`${u}#${A.id}`,k=(A,F)=>!a&&A.id!=="faq"?Jv(e,F):F,R=A=>a?A:Jv(e,A),C=A=>A.links?.length?t.jsx("div",{className:"guide-link-row",children:A.links.map(F=>t.jsxs("span",{className:"guide-link-item",children:[F.prefix?t.jsx("span",{children:k(A,F.prefix)}):null,t.jsx("a",{href:F.href,target:F.href.startsWith("/")?void 0:"_blank",rel:F.href.startsWith("/")?void 0:"nofollow noopener noreferrer",onClick:F.href.startsWith("/")?$=>{$.preventDefault(),fi(F.href)}:void 0,children:k(A,F.label)}),F.note?t.jsx("span",{children:k(A,F.note)}):null]},F.href))}):null,U=A=>t.jsxs("div",{className:"panel-body guide-steps",children:[t.jsx("p",{children:k(A,A.body)}),A.content?.length?t.jsx("div",{className:"guide-content",children:A.content.map((F,$)=>{if(F.type==="heading")return t.jsx("h2",{children:k(A,F.text)},`${F.type}-${$}`);if(F.type==="paragraph")return t.jsx("p",{children:k(A,F.text)},`${F.type}-${$}`);const Q=F.image;return t.jsxs("figure",{className:"guide-image",children:[Q.title?t.jsx("h2",{children:k(A,Q.title)}):null,t.jsx("img",{src:Q.src,alt:k(A,Q.alt),loading:"lazy",decoding:"async"}),Q.caption?t.jsx("figcaption",{children:k(A,Q.caption)}):null]},`${F.type}-${Q.src}`)})}):t.jsxs(t.Fragment,{children:[A.steps?.length?t.jsx("ol",{children:A.steps.map(F=>t.jsx("li",{children:k(A,F)},F))}):null,A.code?t.jsx("pre",{className:"guide-code",children:A.code}):null,A.cards?.length?t.jsx("div",{className:"guide-format-grid",children:A.cards.map(F=>t.jsxs("div",{children:[t.jsx("strong",{children:k(A,F.title)}),t.jsx("span",{children:k(A,F.body)})]},F.title))}):null,A.images?.length?t.jsx("div",{className:"guide-image-list",children:A.images.map(F=>t.jsxs("figure",{className:"guide-image",children:[F.title?t.jsx("h2",{children:k(A,F.title)}):null,t.jsx("img",{src:F.src,alt:k(A,F.alt),loading:"lazy",decoding:"async"}),F.caption?t.jsx("figcaption",{children:k(A,F.caption)}):null]},F.src))}):null]}),A.faqGroups?.length?t.jsx("div",{className:"guide-faq-groups",children:A.faqGroups.map((F,$)=>t.jsxs(m.Fragment,{children:[t.jsxs("section",{className:"guide-faq-group",children:[t.jsx("h2",{children:F.title}),t.jsx("div",{className:"guide-faq-list",children:F.items.map(Q=>t.jsxs("article",{className:"guide-faq-item",children:[t.jsx("h3",{children:Q.question}),Q.paragraphs.map(J=>t.jsx("p",{children:J},J)),Q.bullets?.length?t.jsx("ul",{children:Q.bullets.map(J=>t.jsx("li",{children:J},J))}):null,Q.links?.length?t.jsx("div",{className:"guide-faq-links",children:Q.links.map(J=>{const le=J.href.startsWith("/");return t.jsxs("span",{className:"guide-link-item",children:[t.jsx("a",{href:J.href,target:le?void 0:"_blank",rel:le?void 0:"nofollow noopener noreferrer",onClick:le?de=>{de.preventDefault(),fi(J.href)}:void 0,children:J.label}),J.note?t.jsx("span",{children:J.note}):null]},J.href)})}):null]},Q.question))})]}),$===0&&x?.position==="inline"?t.jsx(hd,{placement:x}):null]},F.title))}):null]});return t.jsxs("main",{className:"app-shell compact-shell",children:[t.jsx(Co,{active:"GUIDE",authLoading:Ve,user:ee,onMenu:()=>gi("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar news-toolbar",children:[t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:a?"TECHNICAL DOCUMENTATION":R("PLAYER GUIDE")}),t.jsx("strong",{children:a?"Docs":R("Guide")})]}),t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),g?t.jsxs("section",{className:`guide-article-layout${x?" guide-article-layout-with-venatus-review":""}${x?.position==="rail"?" guide-article-layout-with-ad":""}`,children:[x?.position==="top"?t.jsx(hd,{placement:x}
156):null,t.jsxs("aside",{className:"guide-directory-panel","aria-label":a?"Technical documentation":"Guide articles",children:[t.jsx("a",{className:"guide-directory-back",href:u,onClick:A=>{A.preventDefault(),fi(u)},children:a?"All documentation":R("All guides")}),t.jsx("nav",{className:"guide-directory-list",children:s.map(A=>{const F=S(A);return t.jsxs("a",{className:A.id===g.id?"active":"",href:F,onClick:$=>{$.preventDefault(),fi(F)},children:[t.jsx("span",{children:k(A,A.kicker)}),t.jsx("strong",{children:k(A,A.title)})]},A.id)})})]}),t.jsxs("article",{className:"panel guide-card guide-article-card",id:g.id,children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:k(g,g.kicker)}),t.jsx("h1",{children:k(g,g.title)}),C(g)]}),x?.position==="article"?t.jsx(hd,{placement:x}):null,U(g)]}),x?.position==="rail"?t.jsx(hd,{placement:x}):null]}):t.jsxs("section",{className:"guide-layout guide-index-layout","aria-label":a?"Voxalike technical documentation":"Voxalike guide articles",children:[t.jsxs("article",{className:"panel guide-card guide-index-intro",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:a?"VOXALIKE DOCS":"VOXALIKE GUIDE"}),t.jsx("h1",{children:R(n.title)})]}),t.jsx("div",{className:"panel-body guide-steps",children:t.jsx("p",{children:R(n.description)})})]}),t.jsxs("div",{className:"guide-article-grid",children:[s.map(A=>{const F=S(A);return t.jsxs("a",{className:"guide-article-link",href:F,onClick:$=>{$.preventDefault(),fi(F)},children:[t.jsx("span",{children:k(A,A.kicker)}),t.jsx("strong",{children:k(A,A.title)}),t.jsx("small",{children:k(A,A.body)})]},A.id)}),a?null:t.jsxs("a",{className:"guide-article-link",href:"/docs",onClick:A=>{A.preventDefault(),fi("/docs")},children:[t.jsx("span",{children:R("FOR DEVELOPERS")}),t.jsx("strong",{children:R("Technical Documentation")}),t.jsx("small",{children:R("Dub Pack and Judge Pack format references.")})]})]})]}),t.jsx(qo,{})]})}if(T==="maker")return new URLSearchParams(window.location.search).get("embed")==="creator"?t.jsxs("main",{className:"app-shell compact-shell dub-maker-page dub-maker-embedded",children:[t.jsxs("div",{className:"screen-toolbar dub-maker-toolbar",children:[t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:"LOCAL CREATOR TOOL"}),t.jsx("h1",{children:"Dub Mod Maker"})]}),t.jsx("a",{className:"ghost-button",href:"/guide/make-dub-mods",target:"_top",children:"Format Guide"})]}),t.jsx(Nv,{})]}):t.jsxs("main",{className:"app-shell compact-shell dub-maker-page",children:[t.jsx(Co,{active:"",authLoading:Ve,user:ee,onMenu:()=>gi("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar dub-maker-toolbar",children:[t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:"LOCAL CREATOR TOOL"}),t.jsx("h1",{children:"Dub Mod Maker"})]}),t.jsx("a",{className:"ghost-button",href:"/guide/make-dub-mods",children:"Format Guide"})]}),t.jsx(Nv,{}),t.jsx(qo,{})]});async function YP(a){a.preventDefault(),Eu(void 0),Au(""),zg(!0);try{Eu(await yM(Lg))}catch(n){Au(n instanceof Error?n.message:r("Could not find that player."))}finally{zg(!1)}}function KP(){const a=er.filter(d=>d.status==="active").map(d=>d.code);
156if(!a.length)return;const n=URL.createObjectURL(new Blob([`${a.join(` 157`)} 158`],{type:"text/plain;charset=utf-8"})),s=document.createElement("a");s.href=n,s.download="voxalike-supporter-gift-codes.txt",s.click(),URL.revokeObjectURL(n)}if(T==="support"){const a=ee?.supporterIdentities.includes("supporter")??!1,n=new URLSearchParams(window.location.search),s=n.get("payment"),d=n.get("recipient")||r("the selected player"),u=n.get("redeem"),h=s==="error"?r("Checkout could not start. Please try again."):s==="rate_limited"?r("Too many checkout attempts. Please wait a few minutes."):s==="unavailable"?r("Supporter checkout is not available yet."):s==="gift_unavailable"?r("Gift code checkout is not available yet."):s==="already_supported"?r("Supporter is already active on this account."):s==="canceled"?r("Checkout was canceled. Nothing was charged."):s==="return"&&!a?r("Payment is being confirmed. Your benefits will appear here automatically."):s==="gift_return"&&Eg?r("Payment is being confirmed. Your gift codes will appear in the gift manager automatically."):s==="gift_sent"?cx?r("Supporter was delivered to {name}.",{name:d}):r("Payment is being confirmed. The gift will be delivered to {name} automatically.",{name:d}):s==="recipient_supported"?r("That player already has Supporter."):s==="gift_self"?r("Use Activate on this account to buy Supporter for yourself."):s==="gift_recipient_missing"?r("That Voxalike account is no longer available."):"",x=u==="success"?r("Code redeemed. Supporter is now active."):u==="invalid"?r("This code is invalid or is not ready yet."):u==="used"?r("This code has already been redeemed."):u==="rate_limited"?r("Too many redemption attempts. Please wait a few minutes."):u==="already_supported"?r("Supporter is already active on this account."):"",g=ee?`/workshop/creators/${encodeURIComponent(ee.id)}/edit#supporter-style`:"/support",S=ee?.displayName||r("Voxalike Player");return t.jsxs("main",{className:"app-shell compact-shell support-shell",children:[t.jsx(Co,{active:"SUPPORTER",authLoading:Ve,user:ee,onMenu:()=>gi("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar news-toolbar support-toolbar",children:[t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:r("Support Voxalike")}),t.jsx("h1",{id:"support-title",children:"Supporter"})]}),t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),t.jsx("section",{className:"support-page","aria-labelledby":"support-title",children:t.jsxs("article",{className:"support-hero",children:[t.jsxs("div",{className:"support-hero-copy",children:[t.jsxs("div",{className:"support-profile-preview","aria-label":r("Supporter profile preview"),children:[t.jsx("div",{className:`support-preview-avatar ${eu(Tg)}`,children:ee?.avatarUrl?t.jsx("img",{src:ee.avatarUrl,alt:""}):t.jsx("span",{children:S.slice(0,1).toUpperCase()})}),t.jsxs("div",{children:[t.jsx("small",{children:r("Profile preview")}),t.jsx(_o,{name:S,identity:"supporter",nameColor:ee?.supporterNameColor??"violet"})]})]}),t.jsxs("div",{className:"support-cosmetic-preview",children:[t.jsxs("div",{children:[t.jsx("small",{children:r("6 name colors")}),t.jsx("div",{className:"support-color-row","aria-hidden":"true",children:oS.map(k=>t.jsx("span",{className:`support-color-dot ${aS(k)}`},k))})]}),t.jsxs("div",{children:[t.jsx("small",{children:r("3 avatar frames + 1 animated bonus frame")}),t.jsx("div",{className:"support-frame-row",children:rS.map(k=>t.jsx("button",{className:`support-frame-dot ${eu(k)}`,type:"button","aria-label":r("Try on {frame}",{frame:uv[k]}),"aria-pressed":Tg===k,title:r("Try on {frame}",{frame:uv[k]}),onClick:()=>ox(k)},k))})]})]})]}),t.jsxs("aside",{className:"support-offer-card","aria-label":r("Supporter purchase"),children:[t.jsxs("div",{className:"support-price",children:[t.jsxs("div",{children:[t.jsx("strong",{children:"US$5"}),t.jsx("small",{children:r("one time")})]}),t.jsx("span",{children:r("No subscription")})]}),t.jsxs("ul",{className:"support-benefits",children:[t.jsxs("li",{children:[t.jsx(nt,{"aria-hidden":"true",size:18}),t.jsx("span",{children:r("Ad-free on this account")}
158)]}),t.jsxs("li",{children:[t.jsx(nt,{"aria-hidden":"true",size:18}),t.jsx("span",{children:r("Animated Supporter badge")})]}),t.jsxs("li",{children:[t.jsx(nt,{"aria-hidden":"true",size:18}),t.jsx("span",{children:r("6 name colors")})]}),t.jsxs("li",{children:[t.jsx(nt,{"aria-hidden":"true",size:18}),t.jsx("span",{children:r("3 avatar frames + 1 animated bonus frame")})]})]}),h?t.jsx("p",{className:"support-message",role:"status",children:h}):null,a?t.jsxs("div",{className:"support-active",children:[t.jsx(nt,{"aria-hidden":"true",size:19}),t.jsx("strong",{children:r("Supporter active")})]}):null,Ve?t.jsx("button",{className:"support-checkout",type:"button",disabled:!0,children:r("Loading account...")}):ee?ee.supporterCheckoutVisible?t.jsxs("div",{className:`support-purchase-actions${a?" gift-only":""}`,children:[a?null:t.jsx("form",{method:"post",action:"/api/payments/checkout",children:t.jsxs("button",{className:"support-purchase-option direct",type:"submit",name:"purchaseType",value:"direct",children:[t.jsx(JC,{"aria-hidden":"true",size:18}),t.jsx("strong",{children:r("Activate on this account")}
158)]})}),t.jsxs("button",{className:"support-purchase-option gift",type:"button",onClick:()=>Ig(!0),children:[t.jsx(gm,{"aria-hidden":"true",size:18}),t.jsx("strong",{children:r("Give Supporter")})]})]}):a?null:t.jsx("button",{className:"support-checkout",type:"button",disabled:!0,children:r("Supporter checkout is not available yet.")}):t.jsx("button",{className:"support-checkout",type:"button",onClick:ft,children:r("Sign in to continue")}),a?t.jsx("a",{className:"support-style-link",href:g,children:r("Customize style")}):null,er.length?t.jsxs("button",{className:"support-gift-manager-link",type:"button",onClick:()=>Ng(!0),children:[t.jsx(gm,{"aria-hidden":"true",size:17}),t.jsx("span",{children:r("Manage gift codes")}),t.jsx("strong",{children:er.filter(k=>k.status==="active").length})]}):null,u?t.jsxs("section",{className:"support-redeem","aria-labelledby":"support-redeem-title",children:[t.jsxs("div",{children:[t.jsx(Vd,{"aria-hidden":"true",size:18}),t.jsx("h3",{id:"support-redeem-title",children:r("Redeem Supporter code")})]}),x?t.jsx("p",{className:`support-redeem-message${u==="success"?" success":""}`,role:"status",children:x}):null,!a&&ee?t.jsxs("form",{method:"post",action:"/api/supporter/codes",children:[t.jsx("label",{htmlFor:"supporter-code",children:r("Enter Supporter code")}),t.jsxs("div",{children:[t.jsx("input",{id:"supporter-code",name:"code",type:"text",inputMode:"text",autoComplete:"off",maxLength:32,placeholder:"VOXA-XXXX-XXXX-XXXX",required:!0}),t.jsx("button",{type:"submit",children:r("Redeem code")})]})]}):null]}):null,ee?.supporterCheckoutVisible?t.jsxs("small",{className:"support-legal",children:[r("Voxalike is operated by QHyralis, LLC, the payment recipient."),t.jsx("br",{}),r("By continuing, you agree to the")," ",t.jsx("a",{href:"/terms",children:r("Terms")})," ",r("and")," ",t.jsx("a",{href:"/privacy",children:r("Privacy Policy")}),"."]}):null]})]})}),nx?t.jsx("div",{className:"support-modal-backdrop",role:"presentation",children:t.jsxs("section",{className:"support-modal",role:"dialog","aria-modal":"true","aria-labelledby":"support-gift-title",children:[t.jsx("button",{className:"support-modal-close",type:"button","aria-label":r("Close"),onClick:()=>Ig(!1),children:t.jsx(Tt,{"aria-hidden":"true",size:20})}),t.jsxs("div",{className:"support-modal-heading",children:[t.jsx(gm,{"aria-hidden":"true",size:22}),t.jsxs("div",{children:[t.jsx("small",{children:r("SUPPORTER GIFT")}),t.jsx("h2",{id:"support-gift-title",children:r("Give Supporter")})]})]}),t.jsxs("div",{className:"support-gift-mode-tabs",role:"tablist","aria-label":r("Gift delivery method"),children:[t.jsxs("button",{className:as==="account"?"active":"",type:"button",role:"tab","aria-selected":as==="account",onClick:()=>Dg("account"),children:[t.jsx(pw,{"aria-hidden":"true",size:18}),t.jsxs("span",{children:[t.jsx("strong",{children:r("Send to a player")}),t.jsx("small",{children:r("Delivered directly to their account")})]})]}),t.jsxs("button",{className:as==="code"?"active":"",type:"button",role:"tab","aria-selected":as==="code",onClick:()=>Dg("code"),children:[t.jsx(Vd,{"aria-hidden":"true",size:18}),t.jsxs("span",{children:[t.jsx("strong",{children:r("Buy gift codes")}),t.jsx("small",{children:r("Share the codes yourself")})]})]})]}),as==="account"?t.jsxs("div",{className:"support-gift-account-panel",children:[t.jsxs("form",{className:"support-recipient-search",onSubmit:k=>{YP(k)},children:[t.jsx("label",{htmlFor:"support-gift-email",children:r("Player's Voxalike email")}),t.jsxs("div",{children:[t.jsx("input",{id:"support-gift-email",type:"email",value:Lg,onChange:k=>{sx(k.target.value),Eu(void 0),Au("")},autoComplete:"off",required:!0}),t.jsxs("button",{type:"submit",disabled:Vg,children:[t.jsx(dd,{"aria-hidden":"true",size:16}),r(Vg?"Searching...":"Find player")]})]})]}),Og?t.jsx("p",{className:"support-recipient-error",role:"alert",children:Og}):null,jr?t.jsxs("article",{className:"support-recipient-card",children:[t.jsx("div",{className:"support-recipient-avatar",children:jr.avatarUrl?t.jsx("img",{src:jr.avatarUrl,alt:""}):t.jsx("span",{children:jr.displayName.slice(0,1).toUpperCase()})}),t.jsxs("div",{children:[t.jsx("small",{children:r("Confirm recipient")}),t.jsx("strong",{children:jr.displayName}),t.jsx("a",{href:jr.profileUrl,target:"_blank",rel:"noreferrer",children:r("Open profile")})]}),jr.isSelf?t.jsx("span",{className:"support-recipient-status warning",children:r("This is your account")}
158):jr.supporterActive?t.jsx("span",{className:"support-recipient-status warning",children:r("Already has Supporter")}):t.jsxs("form",{method:"post",action:"/api/payments/checkout",children:[t.jsx("input",{type:"hidden",name:"purchaseType",value:"gift"}),t.jsx("input",{type:"hidden",name:"giftDelivery",value:"account"}),t.jsx("input",{type:"hidden",name:"giftRecipientUserId",value:jr.id}),t.jsx("button",{className:"support-gift-buy",type:"submit",children:r("Gift Supporter for US$5")})]})]}):null]}):t.jsxs("form",{className:"support-code-purchase",method:"post",action:"/api/payments/checkout",children:[t.jsx("input",{type:"hidden",name:"purchaseType",value:"gift"}),t.jsx("input",{type:"hidden",name:"giftDelivery",value:"code"}),t.jsx("label",{htmlFor:"support-gift-quantity",children:r("Number of gift codes")}),t.jsxs("div",{className:"support-quantity-row",children:[t.jsx("button",{type:"button",onClick:()=>Tu(k=>Math.max(1,k-1)),disabled:_n<=1,children:"â"}),t.jsx("input",{id:"support-gift-quantity",name:"giftQuantity",type:"number",min:"1",max:"20",value:_n,onChange:k=>Tu(Math.min(20,Math.max(1,Number(k.target.value)||1)))}),t.jsx("button",{type:"button",onClick:()=>Tu(k=>Math.min(20,k+1)),disabled:_n>=20,children:"+"})]}),t.jsxs("div",{className:"support-code-total",children:[t.jsx("span",{children:r("Total")}),t.jsxs("strong",{children:["US$",_n*5]})]}),t.jsx("button",{className:"support-gift-buy",type:"submit",children:_n===1?r("Buy 1 gift code"):r("Buy {count} gift codes",{count:_n})})]})]})}):null,ix?t.jsx("div",{className:"support-modal-backdrop",role:"presentation",children:t.jsxs("section",{className:"support-modal support-code-manager",role:"dialog","aria-modal":"true","aria-labelledby":"support-code-manager-title",children:[t.jsx("button",{className:"support-modal-close",type:"button","aria-label":r("Close"),onClick:()=>Ng(!1),children:t.jsx(Tt,{"aria-hidden":"true",size:20})}),t.jsxs("div",{className:"support-modal-heading",children:[t.jsx(Vd,{"aria-hidden":"true",size:22}),t.jsxs("div",{children:[t.jsx("small",{children:r("SUPPORTER GIFTS")}),t.jsx("h2",{id:"support-code-manager-title",children:r("Your gift codes")})]})]}),t.jsxs("div",{className:"support-code-manager-actions",children:[t.jsx("span",{children:er.filter(k=>k.status==="active").length===1?r("1 unused code"):r("{count} unused codes",{count:er.filter(k=>k.status==="active").length})}),t.jsxs("div",{children:[t.jsxs("button",{type:"button",disabled:!er.some(k=>k.status==="active"),onClick:()=>{navigator.clipboard.writeText(er.filter(k=>k.status==="active").map(k=>k.code).join(` 159`))},children:[t.jsx(Ga,{"aria-hidden":"true",size:15}),r("Copy all unused")]}),t.jsxs("button",{type:"button",disabled:!er.some(k=>k.status==="active"),onClick:KP,children:[t.jsx(Gt,{"aria-hidden":"true",size:15}),r("Download unused")]})]})]}),t.jsx("div",{className:"support-gift-code-list",children:Eg?t.jsx("p",{children:r("Loading gift codes...")}):er.length?er.map(k=>t.jsxs("div",{className:"support-gift-code",children:[t.jsx("code",{children:k.code}),t.jsx("span",{children:r(k.status==="redeemed"?"Redeemed":"Ready to redeem")}),t.jsxs("button",{type:"button",disabled:k.status==="redeemed",onClick:()=>{navigator.clipboard.writeText(k.code).then(()=>{Ag(k.id),window.setTimeout(()=>Ag(R=>R===k.id?void 0:R),2e3)})},children:[t.jsx(Ga,{"aria-hidden":"true",size:15}),r(ax===k.id?"Copied":"Copy code")]})]},k.id)):t.jsx("p",{children:r("You do not have any gift codes yet.")})})]})}):null,t.jsx(qo,{})]})}if(T==="workshop"){const a=Cu==="judge"?"judge":"dub",n=a==="judge",s=n?ts:Yt,d=n?"Judge Mods":"Dub Mods",u=Su?"Loading...":`${s.length} ${s.length===1?"mod":"mods"}`,h=new Map(pt.map(g=>[g.id,g])),x=new Map(tn.map(g=>[g.id,g]));return t.jsxs("main",{className:"app-shell compact-shell",children:[t.jsx(Co,{active:"WORKSHOP",authLoading:Ve,user:ee,onMenu:()=>Y("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar news-toolbar",children:[t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:"COMMUNITY MODS"}),t.jsx("strong",{children:"Workshop"})]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{NP()},disabled:Su,children:[t.jsx(it,{size:18}),"Refresh"]})]}),t.jsx("section",{className:"workshop-layout",children:t.jsxs("div",{className:"panel mod-library-panel workshop-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("h2",{children:d}),t.jsx("p",{children:u})]}),$c?t.jsx("p",{className:"workshop-message",role:"status",children:r($c)}):null,t.jsxs("div",{className:"mod-type-tabs workshop-mod-type-tabs","aria-label":"Workshop mod type",children:[t.jsxs("button",{className:a==="dub"?"active":"",type:"button","aria-pressed":a==="dub",onClick:()=>on("dub"),children:[t.jsx("span",{children:"Dub Mods"}),t.jsx("strong",{children:Yt.length})]}),t.jsxs("button",{className:a==="judge"?"active":"",type:"button","aria-pressed":a==="judge",onClick:()=>on("judge"),children:[t.jsx("span",{children:"Judge Mods"}),t.jsx("strong",{children:ts.length})]})]}),t.jsx("div",{className:"panel-body workshop-list",children:!n&&Yt.length?Yt.map(g=>{const S=h.get(g.id),k=!!S,R=k?{...g,subscribed:!0,userRating:S?.userRating??g.userRating}:g,C=Gc===g.id,U=qs("dub",g.id),A=ed("dub",g.id),F=GP(g),$=da.has(g.id),Q=$||U;return t.jsxs("article",{className:"mod-card workshop-card",children:[g.coverUrl?t.jsx("img",{alt:"",className:"workshop-cover",src:g.coverUrl}):t.jsx("div",{className:"workshop-cover workshop-cover-empty",children:t.jsx(bt,{size:34})}),t.jsxs("div",{className:"workshop-card-main",children:[t.jsx("span",{children:"DUB MOD"}),t.jsx("h3",{children:g.title}),t.jsx("p",{children:g.creator}),t.jsxs("div",{className:"workshop-meta",children:[t.jsx("strong",{children:lL(g)}),t.jsx("strong",{children:tw(g.downloadCount)}),g.characters.length?t.jsx("strong",{children:g.characters.slice(0,4).join(", ")}):null]})]}),t.jsx("div",{className:"workshop-card-actions",children:t.jsxs("button",{className:`action-button ${C||U?"installing-button":F?"installed-button":""}`,type:"button",disabled:Q||F,onClick:()=>{k?Ci(R):Hb(g,!0)},title:F?"Installed in local library":k?"Retry local installation":"Subscribe and install",children:[F||$?t.jsx(nt,{size:18}
159):U?null:t.jsx(Gt,{size:18}),$?"Subscribed":U?t.jsx(jn,{percent:go.packId===g.id?go.percent:void 0,pendingLabel:C||A?"Installing...":"Waiting..."}):F?"Installed":k?"Retry Install":"Subscribe"]})})]},g.id)}):n&&ts.length?ts.map(g=>{const k=!!x.get(g.id),R=k?{...g,subscribed:!0}:g,C=bg===g.id,U=qs("judge",g.id),A=ed("judge",g.id),F=qP(g),$=os.has(g.id),Q=$||U;return t.jsxs("article",{className:"mod-card workshop-card",children:[g.previewImageUrl?t.jsx("img",{alt:"",className:"workshop-cover judge-mod-cover",src:g.previewImageUrl}):t.jsx("div",{className:"workshop-cover workshop-cover-empty judge-mod-cover",children:t.jsx(Ua,{size:34})}),t.jsxs("div",{className:"workshop-card-main",children:[t.jsx("span",{children:"JUDGE MOD"}),t.jsx("h3",{children:g.title}),t.jsx("p",{children:g.author||"Unknown author"}),t.jsxs("div",{className:"workshop-meta",children:[t.jsxs("strong",{children:[g.imageCount," images"]}),t.jsxs("strong",{children:[g.soundCount," sounds"]}),t.jsxs("strong",{children:[g.feedbackCount," lines"]}),t.jsx("strong",{children:tw(g.downloadCount)})]})]}),t.jsx("div",{className:"workshop-card-actions",children:t.jsxs("button",{className:`action-button ${C||U?"installing-button":F?"installed-button":""}`,type:"button",disabled:Q||F,onClick:()=>{k?td(R):Jb(g,!0)},title:F?"Installed in local library":k?"Retry local installation":"Subscribe and install",children:[F||$?t.jsx(nt,{size:18}):U?null:t.jsx(Gt,{size:18}),$?"Subscribed":U?t.jsx(jn,{percent:go.packId===g.id?go.percent:void 0,pendingLabel:C||A?"Installing...":"Waiting..."}):F?"Installed":k?"Retry Install":"Subscribe"]})})]},g.id)}):t.jsxs("div",{className:"workshop-empty",children:[n?t.jsx(Ua,{size:34}):t.jsx(bt,{size:34}),t.jsx("strong",{children:Su?"Loading workshop...":n?"No judge mods yet":"No dub mods yet"}),t.jsx("p",{children:n?"Judge Mods will show up here after creators upload them.":"Dub Mods will show up here after creators upload them."})]})})]})}),t.jsx(qo,{})]})}if(T==="mods")return t.jsxs("main",{className:"app-shell compact-shell mobile-workspace-shell mods-shell",children:[t.jsx(Co,{active:"MODS",authLoading:Ve,user:ee,onMenu:()=>Y("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>Y("menu"),title:r("Back"),children:[t.jsx(Go,{size:18}),r("Menu")]}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:r("MOD MANAGER")}),t.jsx("strong",{children:r("Mods")})]}),t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),rC(),t.jsx(qo,{}),hy()]});if(T==="settings")return t.jsxs("main",{className:"app-shell compact-shell mobile-workspace-shell settings-shell",children:[t.jsx(Co,{active:"SETTINGS",authLoading:Ve,user:ee,onMenu:()=>Y("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>Y("menu"),title:r("Back"),children:[t.jsx(Go,{size:18}),r("Menu")]}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:r("PLAYER OPTIONS")}),t.jsx("strong",{children:r("Settings")})]}),t.jsx("span",{className:"settings-fullscreen-hint",children:r("Press F11 for fullscreen.")})]}),t.jsxs("div",{className:"settings-grid",children:[PC(),CC(),MC(),jC()]}),t.jsx(qo,{}),pm()]});if(T==="multi"){const a=tt||ge,n=!!(v?.roleSelectionActive&&!a),s=!a&&!v?.game,d=!a&&!v,u=!!(v&&!v.game&&!a&&!n),h=!v||v.game||q||ge||tt;return t.jsxs("main",{className:`app-shell${a?" immersive-shell":""}${s?" lobby-shell":""}${u?" joined-room-shell":""}${n?" role-selection-shell":""}${ge&&!Rt?" dub-premiere-shell":""}`,children:[a?null:t.jsx(Co,{active:"MULTIPLAYER",authLoading:Ve,user:ee,onMenu:()=>Y("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar",children:[h?t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{Jx()},title:r("Back"),children:[t.jsx(Go,{size:18}),r("Back")]}):t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}),v&&!v.game&&!q&&!ge?t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"}):t.jsx("div",{className:"screen-title",children:t.jsxs(t.Fragment,{children:[t.jsx("span",{children:r("MULTIPLAYER")}),t.jsx("strong",{children:r(sn?"Recent Dub":ge?Rt?ms?"Final Score":"Judge Score":"My Dub Premiere":q?"Dub Stage":gf||ya?"Preparing Stage":tt?"Dub Stage":"Create or Join")})]})}),t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),t.jsxs("div",{className:`app-grid room-game-grid${u?" joined-lobby-grid":""}${d?" pre-room-grid":""}${n?" role-selection-grid":""}`,children:[a||n?null:t.jsx("aside",{className:"control-stack",children:xC()}),d?null:t.jsx("div",{className:`round-slot${a?" full-run-slot":""}`,children:ge?!sn&&Ge(v?.game)==="pvp"?hC():uy():q?ay():gC()})]}),v?.status==="playing"?t.jsx(pT,{connected:Er,error:Bu,messages:Fu,players:v.players,onlineCount:zm(v),onSend:Op,leadingControl:sC()}):null,t.jsx(hT,{message:Cx}),a?t.jsx(Nk,{}):null,bC(),fC(),mC(),yC(),pm()]})}const ad=rr||tt||ge,ZP=ge&&Rt&&ms;return t.jsx(t.Fragment,{children:t.jsxs("main",{className:`app-shell single-player
159-shell${ad?" immersive-shell":""}${ge&&!Rt?" dub-premiere-shell":""}${rr?" single-start-loading-shell":""}`,children:[ad?null:t.jsx(Co,{active:"PLAY",authLoading:Ve,user:ee,onMenu:()=>Y("menu"),onNavigate:Ot,onSignIn:ft,onSignOut:()=>{ur()}}),t.jsxs("div",{className:"screen-toolbar",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:q||ge?zf:()=>Y("menu"),title:r("Back"),children:[t.jsx(Go,{size:18}),r("Back")]}),t.jsx("div",{className:"screen-title",children:rr||ge||q?t.jsxs(t.Fragment,{children:[t.jsx("span",{children:r("SINGLE PLAYER")}),t.jsx("strong",{children:r(rr?"Preparing Stage":ge?Rt?ms?"Final Score":"Judge Score":"My Dub Premiere":"Dub Stage")})]}):t.jsx("strong",{children:r("SINGLE PLAYER")})}),ZP?t.jsxs("button",{className:"action-button",type:"button",onClick:()=>{Np()},title:r("New Run"),children:[t.jsx(_e,{size:18}),r("New Run")]}):t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),rr?XP():ge?uy():q?ay():eC(),ad?null:t.jsx(qo,{}),ad?t.jsx(Nk,{}):null,QP(),hy(),pm()]})});function XP(){return t.jsx("section",{className:"panel single-start-loading-stage",role:"status","aria-label":"Preparing stage","aria-live":"polite",children:t.jsxs("div",{className:"single-meteor-loader","aria-hidden":"true",children:[t.jsx("span",{className:"single-meteor single-meteor-one"}),t.jsx("span",{className:"single-meteor single-meteor-two"}),t.jsx("span",{className:"single-meteor single-meteor-three"}),t.jsx("span",{className:"single-meteor single-meteor-four"}),t.jsx("span",{className:"single-meteor single-meteor-five"}),t.jsxs("span",{className:"single-meteor-core",children:[t.jsx("i",{}),t.jsx("i",{})]})]})})}function QP(){if(!Ax||T!=="single"||!tt||!H)return null;const a=La(H.clips.length),n=a.indexOf(xt),s=Ix,d=s===void 0?0:Math.max(0,s-n);return t.jsx("div",{className:"single-clip-picker-backdrop",role:"presentation",onMouseDown:u=>{u.currentTarget===u.target&&!pn&&(gs(!1),un(void 0))},children:t.jsxs("section",{className:"single-clip-picker-modal",role:"dialog","aria-modal":"true","aria-labelledby":"single-clip-picker-title",children:[t.jsxs("header",{className:"single-clip-picker-header",children:[t.jsxs("div",{children:[t.jsx("span",{children:"QUICK JUMP"}),t.jsx("h2",{id:"single-clip-picker-title",children:"Choose a clip"}),t.jsx("p",{children:"Skipped clips keep their original audio."})]}),t.jsx("button",{className:"ghost-button icon-button",type:"button",disabled:pn,onClick:()=>{gs(!1),un(void 0)},"aria-label":"Close clip picker",title:"Close",children:t.jsx(Tt,{size:18})})]}),t.jsx("div",{className:"single-clip-picker-list",children:a.map((u,h)=>{const x=H.clips[u];if(!x)return null;const g=h===n,S=h<n,k=h===s,R=Hk(x.speakers);return t.jsxs("button",{className:`single-clip-picker-row${g?" current":""}${k?" selected":""}`,type:"button",disabled:S||g||pn,onClick:()=>un(h),children:[t.jsx("strong",{children:String(h+1).padStart(2,"0")}),t.jsxs("span",{children:[t.jsx("b",{children:R||"Unknown character"}),t.jsx("small",{children:x.caption||x.title})]}),g?t.jsx("em",{children:"Current"}):k?t.jsx("em",{children:"Selected"}):null]},`${x.id}:${h}`)})}),t.jsxs("footer",{className:"single-clip-picker-footer",children:[t.jsx("p",{children:s===void 0?"Select a later clip.":`Keep original audio for ${d} ${d===1?"clip":"clips"} and jump to Clip ${s+1}?`}),t.jsxs("div",{className:"button-row",children:[t.jsx("button",{className:"ghost-button",type:"button",disabled:pn,onClick:()=>un(void 0),children:"Cancel"}),t.jsx("button",{className:"action-button",type:"button",disabled:s===void 0||pn,onClick:()=>s!==void 0&&void eP(s),children:pn?"Skipping...":"Continue"})]})]})]})})}function eC(){const a=Rn(),n=Va(),s=a.find(J=>J.id===Tr)??a.find(J=>J.id===ye)??a[0],d=s?s.id===ye&&H?Bo(H):s.summary:n?Bo(n):void 0,u=d?.title||s?.name||n?.title,h=s?cL(s,d):n?sL(n):{creator:"",characters:[]},x=d?.clipCount??n?.clips.length,g=d?.videoCount??(n?Id(n.clips):void 0),S=d?.videoDurationSeconds??(n?Qm(n.clips):void 0),k=rr||!s&&!n?.clips[0],R=a.length>0||!!n,C=pt.filter(J=>!ji(J)&&(Gc===J.id||qs("dub",J.id)||da.has(J.id))).length,U=!R&&C>0,A=C===1?"Your mod is downloading":"Your mods are downloading",F=C===1?"Your subscribed mod is still installing. Check its progress in Mods.":"Your subscribed mods are still installing. Check their progress in Mods.",$=J=>oi?t.jsxs("article",{className:`dub-draft-recovery dub-draft-recovery-${J}`,"aria-live":"polite",children:[t.jsxs("div",{className:"dub-draft-recovery-copy",children:[t.jsx("div",{className:"dub-draft-recovery-heading",children:t.jsxs("span",{className:"dub-draft-recovery-label",children:[t.jsx(it,{size:13})," RECONNECT"]})}),t.jsxs("p",{children:[oi.packTitle," · Saved ",PD(oi.updatedAt)]})]}),t.jsxs("div",{className:"dub-draft-recovery-actions",children:[t.jsxs("button",{className:"action-button",type:"button",onClick:()=>{Xx()},disabled:vs,children:[t.jsx(_e,{size:17}),vs?"Reconnecting...":"Reconnect"]}),t.jsx("button",{className:"ghost-button icon-button dub-draft-discard",type:"button",onClick:()=>{Zx()},disabled:vs,"aria-label":"Discard recovered session",title:"Discard recovered session",children:t.jsx(Ho,{size:17})})]})]}):null,Q=s?(s.id===ye?H?.iconUrl:void 0)||rn.current.get(s.id):n?.iconUrl;return t.jsxs("section",{className:`selection-screen${oi?" has-session-recovery":""}${R?"":" empty-library"}`,children:[$("mobile"),R?t.jsx("div",{className:"panel pack-name-panel",children:t.jsx("div",{className:"pack-name-list",children:a.length?a.map(J=>{const le=s?.id===J.id,de=J.id===ye&&H?Bo(H):J.summary;return t.jsx("button",{className:`pack-name-item ${le?"selected":""}`,type:"button",onClick:()=>{ga(J.id),xe(void 0)},title:de?.title||J.name,children:de?.title||J.name},J.id)}):n?t.jsx("button",{className:"pack-name-item selected",type:"button",title:n.title,children:n.title}):null})}):null,t.jsxs("div",{className:"panel pack-detail-panel",children:[u?t.jsxs("article",{className:"pack-detail-card",children:[No?t.jsxs("div",{className:"single-start-error unexpected-run-error",role:"alert",children:[t.jsx(ud,{size:18,"aria-hidden":"true"}),t.jsxs("div",{children:[t.jsx("strong",{children:r("Previous run ended unexpectedly")}),t.jsx("p",{children:r(No.message)})]}),t.jsxs("div",{className:"unexpected-run-error-actions",children:[t.jsx("button",{className:"ghost-button icon-button",type:"button",onClick:()=>{hc(No.technical).then(()=>P("Crash report copied."),()=>P("Could not copy crash report."))},"aria-label":r("Copy crash report"),title:r("Copy crash report"),children:t.jsx(Ga,{size:16})}),t.jsx("button",{className:"ghost-button icon-button",type:"button",onClick:()=>Oc(void 0),"aria-label":r("Dismiss crash report"),title:r("Dismiss crash report"),children:t.jsx(Tt,{size:16})})]})]}):null,Qa?t.jsxs("div",{className:"single-start-error",role:"alert",children:[t.jsx(ud,{size:18,"aria-hidden":"true"}),t.jsxs("div",{children:[t.jsx("strong",{children:r("Could not start this mod")}),t.jsx("p",{children:r(Qa.message)})]}),t.jsx("button",{className:"ghost-button icon-button",type:"button",onClick:()=>{hc(Qa.technical)},"aria-label":r("Copy start error details"),title:r("Copy start error details"),children:t.jsx(Ga,{size:16})})]}):null,t.jsxs("div",{className:"pack-detail-header",children:[t.jsx("div",{className:"pack-cover",children:Q?t.jsx("img",{src:Q,alt:u||"Pack icon",onError:J=>{console.error("Failed to load pack icon:",Q),J.currentTarget.style.display="none"}}):t.jsx("div",{className:"pack-cover-placeholder",children:t.jsx(bt,{size:48})})}),t.jsxs("div",{className:"pack-info",children:[t.jsx("h2",{children:u}),(h.creator||h.characters.length>0)&&t.jsxs("div",{className:"pack-meta",children:[h.creator&&t.jsxs("p",{children:[t.jsx("span",{className:"meta-label",children:r("Creator:")})," ",h.creator]}),h.characters.length>0&&t.jsxs("p",{children:[t.jsx("span",{className:"meta-label",children:r("Characters:")}
159)," ",h.characters.join(", ")]})]}),t.jsxs("div",{className:"pack-stats-inline",children:[t.jsxs("span",{children:[x===void 0?"--":x," clips"]}),g&&g>0?t.jsxs("span",{children:[g," video"]}):null,S?t.jsx("span",{children:KS(S)}):null]})]}),t.jsxs("button",{className:"action-button primary-button pack-start-button",type:"button",onClick:()=>s?void Bf(s):void Np(),disabled:k,title:r("Start"),children:[rr?t.jsx("span",{className:"inline-button-spinner","aria-hidden":"true"}):t.jsx(_e,{size:18}),r(rr?"Starting...":"Start")]})]})]}):t.jsxs("div",{className:"empty-state single-player-mod-empty-state",children:[t.jsx("strong",{children:r(U?A:"No Dub Mods Yet")}),t.jsx("p",{children:U?F:r("Subscribe to a pack from the Workshop, then start your game.")}),t.jsxs("button",{className:"action-button primary-button single-player-mod-empty-action",type:"button",onClick:()=>U?Y("mods"):Ot("workshop"),title:r(U?"View installation progress in Mods":"Browse Workshop"),children:[t.jsx(bt,{size:20}),r(U?"View in Mods":"Browse Workshop")]})]}),$("desktop")]})]})}function tC(){const a=Cu==="judge"?"judge":"theme",n=oe.length,s=pa.length,d=a==="theme"?"Installed Theme Packs":"Installed Judge Mods",u=a==="theme"?z?`${n} saved theme packs / ${z.title} in use`:n?`${n} saved theme packs / default theme in use`:"Default theme in use.":Dt?`${s} saved judge mods / ${Dt.title} in use`:s?`${s} saved judge mods / default judges in use`:"Default judges in use.";return t.jsxs("section",{className:"mods-layout preview-mods-layout",children:[t.jsxs("aside",{className:"panel mod-library-sidebar",children:[t.jsxs("div",{className:"panel-header compact-panel-header",children:[t.jsx("h2",{children:"Mods"}),t.jsxs("p",{children:[n+s," installed"]})]}),t.jsxs("div",{className:"panel-body mod-sidebar-body",children:[t.jsxs("button",{className:"mod-sidebar-tab active",type:"button","aria-pressed":"true",children:[t.jsx("span",{children:"Installed Mods"}),t.jsx("strong",{children:a==="theme"?n:s})]}),t.jsxs("div",{className:"mod-import-menu",children:[a==="theme"?t.jsxs(t.Fragment,{children:[t.jsxs("button",{className:"ghost-button compact-import-button",type:"button",disabled:Nu==="import",onClick:()=>{Qo(!1),Bg.current?.click()},title:"Import a local visual theme ZIP",children:[t.jsx(Ld,{size:17}),Nu==="import"?"Checking...":"Import Theme Pack"]}),t.jsx("p",{className:"mod-import-hint",children:"Theme ZIP with theme.json. Replaces visual assets only."})]}):t.jsxs(t.Fragment,{children:[t.jsxs("button",{className:"ghost-button compact-import-button",type:"button",disabled:ma,onClick:()=>{Qo(!1),_c.current?.click()},title:"Import a Judge Mod ZIP from your device",children:[t.jsx(Ua,{size:17}),ma?"Checking...":"Import Judge Mod"]}),t.jsx("p",{className:"mod-import-hint",children:"Judge ZIP with judge_pack.json. Replaces judge art, text, and sounds only."})]}),t.jsx("input",{className:"hidden-file-input",ref:Bg,type:"file",accept:".zip,application/zip",onChange:h=>{EC(h.currentTarget.files?.[0]),h.currentTarget.value="",on("theme")}}),t.jsx("input",{className:"hidden-file-input",ref:_c,type:"file",accept:".zip,application/zip",onChange:h=>{vy(h.currentTarget.files?.[0]),h.currentTarget.value="",on("judge")}})]}),a==="theme"&&Wg?t.jsx("span",{className:"status-pill compact-warning-pill",children:Wg}):null,a==="judge"&&$g?t.jsx("span",{className:"status-pill compact-warning-pill",children:$g}):null]})]}),t.jsxs("div",{className:"panel mod-library-panel focused-mod-library-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("h2",{children:d}),t.jsx("p",{children:u})]}),t.jsxs("div",{className:"panel-body mod-library-body",children:[t.jsxs("div",{className:"mod-type-tabs","aria-label":"Preview mod type",children:[t.jsxs("button",{className:a==="theme"?"active":"",type:"button","aria-pressed":a==="theme",onClick:()=>{on("theme"),Qo(!1)},children:[t.jsx("span",{children:"Theme Packs"}),t.jsx("strong",{children:n})]}),t.jsxs("button",{className:a==="judge"?"active":"",type:"button","aria-pressed":a==="judge",onClick:()=>{on("judge"),Qo(!1)},children:[t.jsx("span",{children:"Judge Mods"}),t.jsx("strong",{children:s})]})]}),a==="theme"?oC():iC()]})]})]})}function oC(){return _?oe.length?t.jsx("div",{className:"saved-pack-list",children:oe.map(a=>{const n=N===a.id,s=B(a.id),d=!!Nu,u=[a.author?`By ${a.author}`:"",`${a.assetKeys.length} assets`,a.version?`v${a.version}
159`:""].filter(Boolean).join(" / ");return t.jsxs("article",{className:`mod-card saved-pack-card workshop-subscription-card ${n?"active":""}`,children:[s?t.jsx("img",{alt:"",className:"subscription-cover theme-mod-cover",src:s}):t.jsx("div",{className:"subscription-cover workshop-cover-empty theme-mod-cover",children:t.jsx(Ld,{size:24})}),t.jsxs("div",{children:[t.jsx("strong",{children:a.title}),t.jsx("span",{children:u||"Saved locally"})]}),t.jsxs("div",{className:"saved-pack-actions",children:[t.jsxs("button",{className:`action-button ${n?"installed-button":""}`,type:"button","aria-pressed":n,disabled:d,onClick:()=>n?void IC():void AC(a.id),title:n?"Click again to return to the default theme":"Enable Theme Pack",children:[n?t.jsx(nt,{size:16}):t.jsx(_e,{size:16}),n?"In Use":"Enable"]}),t.jsx("button",{className:"danger-button icon-button",type:"button",disabled:d,onClick:()=>{NC(a.id)},title:"Remove from local library",children:t.jsx(Ho,{size:16})})]})]},a.id)})}):t.jsxs("div",{className:"empty-state",children:[t.jsx(Ld,{size:28}),t.jsx("strong",{children:"No theme pack installed"}),t.jsx("p",{children:"Import a Theme Pack ZIP to preview custom menu and stage artwork."})]}):t.jsx("div",{className:"empty-state",children:t.jsx("strong",{children:"Loading theme packs"})})}function rC(){const a=Ve||!!(ee&&ii.current!==ee.id),n=new Set(pt.map(g=>ji(g)?.id).filter(Boolean)),s=new Set(tn.map(g=>Pn(g)?.id).filter(Boolean)),d=Le.filter(g=>!n.has(g.id)&&!Ti(g)).length+pa.filter(g=>!s.has(g.id)&&!Xb(g)).length,u=pt.length+tn.length+d,h=r("{count} mods",{count:u}),x=a||Fc?r("{count} / syncing Workshop",{count:h}):Dt?r("{count} / {name} judges in use",{count:h,name:Dt.title}):r("{count} / default judges in use",{count:h});return t.jsx("section",{className:"mods-layout mods-manager-layout",children:t.jsxs("div",{className:"panel mod-library-panel focused-mod-library-panel",children:[t.jsxs("div",{className:"panel-header mods-library-header",children:[t.jsxs("div",{className:"mods-library-header-copy",children:[t.jsxs("div",{className:"mods-library-title-row",children:[t.jsx("h2",{children:r("My Mods")}),t.jsxs("button",{className:`ghost-button mods-refresh-button ${Mr?"refreshing":""}`,type:"button",disabled:!ee||Ve||Fc||Mr,"aria-label":r(Mr?"Refreshing Mods...":"Refresh Mods"),"aria-busy":Mr,onClick:DP,title:r(Mr?"Refreshing Mods...":"Refresh Mods"),children:[t.jsx(it,{size:15,"aria-hidden":"true"}),t.jsx("span",{className:"mods-refresh-button-label",children:r(Mr?"Refreshing...":"Refresh Mods")})]})]}),t.jsx("p",{children:x})]}),t.jsxs("div",{className:"local-import-overflow",ref:Cf,children:[t.jsx("button",{className:"icon-button local-import-menu-button",type:"button",disabled:ma,"aria-label":r("Local mod import options"),"aria-expanded":Hc,"aria-haspopup":"menu",onClick:()=>Qo(g=>!g),title:r("Local mod import options"),children:t.jsx(lw,{size:18})}),Hc?t.jsxs("div",{className:"mod-import-popover local-import-overflow-menu",role:"menu",children:[t.jsx("span",{className:"local-import-menu-label",children:r("Import local file")}),t.jsxs("button",{className:"ghost-button",type:"button",role:"menuitem",onClick:()=>{Qo(!1),Ug.current?.click()},children:[t.jsx(bt,{size:16}),r("Dub Mod ZIP")]}),t.jsxs("button",{className:"ghost-button",type:"button",role:"menuitem",onClick:()=>{Qo(!1),Fg.current?.click()},children:[t.jsx(bt,{size:16}),r("Dub Mod Folder")]}),t.jsxs("button",{className:"ghost-button",type:"button",role:"menuitem",onClick:()=>{Qo(!1),_c.current?.click()},children:[t.jsx(Ua,{size:16}),r("Judge Mod ZIP")]})]}):null,t.jsx("input",{className:"hidden-file-input",ref:Ug,type:"file",accept:".zip,application/zip",onChange:g=>{BP(g.currentTarget.files?.[0]),g.currentTarget.value=""}}),t.jsx("input",{className:"hidden-file-input",ref:Fg,type:"file",multiple:!0,webkitdirectory:"",directory:"",onCh
159ange:g=>{WP(g.currentTarget.files),g.currentTarget.value=""}}),t.jsx("input",{className:"hidden-file-input",ref:_c,type:"file",accept:".zip,application/zip",onChange:g=>{vy(g.currentTarget.files?.[0]),g.currentTarget.value=""}})]})]}),t.jsxs("div",{className:"panel-body mod-library-body unified-mod-library-body",children:[t.jsxs("div",{className:"mod-search-control",children:[t.jsx(dd,{size:18,"aria-hidden":"true"}),t.jsx("input",{type:"search",value:rs,onChange:g=>Mu(g.currentTarget.value),placeholder:r("Search by mod, creator, character, or type"),"aria-label":r("Search mods")}),rs?t.jsx("button",{className:"icon-button",type:"button",onClick:()=>Mu(""),title:r("Clear search"),children:t.jsx(Tt,{size:16})}):null]}),mx.map(g=>t.jsxs("div",{className:"status-pill compact-warning-pill",role:"status",children:[t.jsx("span",{className:"compact-warning-copy",children:g}),t.jsx("button",{className:"compact-warning-dismiss",type:"button",onClick:()=>tr(S=>S.filter(k=>k!==g)),title:r("Dismiss"),"aria-label":`${r("Dismiss")}: ${g}`,children:t.jsx(Tt,{size:16,"aria-hidden":"true"})})]},g)),$c?t.jsx("p",{className:"workshop-message",role:"status",children:r($c)}):null,aC()]})]})})}function aC(){const a=Ve||!!(ee&&ii.current!==ee.id),n=new Set(pt.map(g=>ji(g)?.id).filter(Boolean)),s=new Set(tn.map(g=>Pn(g)?.id).filter(Boolean)),d=pt.filter(g=>nd("dub dub mod voice pack workshop subscribed",g.title,g.creator,g.description,g.language,g.tags.join(" "),g.characters.join(" "))),u=tn.filter(g=>nd("judge judges judge mod workshop subscribed",g.title,g.author,g.description,g.tags.join(" "))),h=Le.filter(g=>{if(n.has(g.id)||Ti(g))return!1;const k=ye===g.id&&H?Bo(H):g.summary;return nd("dub dub mod voice pack local imported",k?.title,g.name,k?.creator,k?.language,k?.tags?.join(" "),k?.characters?.join(" "))}),x=pa.filter(g=>s.has(g.id)||Xb(g)?!1:nd("judge judges judge mod local imported",g.title,g.author,Object.values(g.names??{}).join(" "),Object.values(g.labels??{}).join(" ")));return!d.length&&!u.length&&!h.length&&!x.length?rs.trim()?nC():a||Fc?t.jsxs("div",{className:"empty-state",children:[t.jsx("strong",{children:r("Syncing Workshop subscriptions")}),t.jsx("p",{children:r("Checking which installed mods belong to your account.")})]}):t.jsxs("div",{className:"empty-state",children:[t.jsx("strong",{children:r("No mods yet")}),t.jsxs("p",{className:"workshop-empty-copy",children:[r("Subscribe to a pack from the Workshop, then start a game in")," ",t.jsx("button",{type:"button",className:"workshop-empty-mode-link",onClick:()=>Ot("single"),children:r("Solo")})," ",r("or")," ",t.jsx("button",{type:"button",className:"workshop-empty-mode-link",onClick:()=>Ot("multi"),children:r("Multiplayer")}),r(" game.")]}),t.jsxs("button",{className:"action-button workshop-empty-action",type:"button",onClick:()=>Ot("workshop"),children:[t.jsx(dd,{size:17}),r("Browse Workshop")]}),ee?null:t.jsx("button",{className:"ghost-button workshop-empty-sign-in",type:"button",onClick:ft,children:r("Sign in")})]}):t.jsxs("div",{className:"saved-pack-list",children:[d.map(g=>{const S=Gc===g.id,k=qs("dub",g.id),R=ed("dub",g.id),C=da.has(g.id),A=!!ji(g);return t.jsxs("article",{className:"mod-card saved-pack-card workshop-subscription-card mod-kind-dub",children:[g.coverUrl?t.jsx("img",{alt:"",className:"subscription-cover",src:g.coverUrl}):t.jsx(bt,{size:26}),t.jsxs("div",{className:"mod-card-copy",children:[t.jsxs("div",{className:"mod-card-title-row",children:[t.jsx("span",{className:"mod-type-badge dub",children:"Dub Mod"}),t.jsx("span",{className:"mod-source-badge workshop",children:"Workshop"}),t.jsx("strong",{children:g.title})]}),t.jsxs("span",{children:[g.creator," / ",g.clipCount," clips"]})]}),t.jsxs("div",{className:"saved-pack-actions",children:[A?null:t.jsxs("button",{className:`action-button ${S||k||C?"installing-button":""}`,type:"button",disabled:k||C,onClick:()=>{Ci(g)},title:"Retry installation",children:[k||C?null:t.jsx(Gt,{size:16}),k||C?t.jsx(jn,{percent:k&&go.packId===g.id?go.percent:void 0,pendingLabel:S||R?"Installing...":"Waiting..."}):"Retry Install"]}),t.jsxs("button",{className:"ghost-button unsubscribe-mod-button",type:"button",disabled:da.has(g.id),onClick:()=>{Hb(g,!1)},title:"Unsubscribe and remove installed mod","aria-label":`Unsubscribe from ${g.title} and remove it`,children:[t.jsx(wc,{className:"unsubscribe-desktop-icon",size:16}),t.jsx(Ho,{className:"unsubscribe-mobile-icon",size:16}),t.jsx("span",{className:"unsubscribe-mod-label",children:"Unsubscribe"})]})]})]},`dub:${g.id}`)}),u.map(g=>
159{const S=bg===g.id,k=qs("judge",g.id),R=ed("judge",g.id),C=os.has(g.id),U=Pn(g),A=!!U,F=!!(U&&Dt?.id===U.id);return t.jsxs("article",{className:"mod-card saved-pack-card workshop-subscription-card mod-kind-judge",children:[g.previewImageUrl?t.jsx("img",{alt:"",className:"subscription-cover judge-mod-cover",src:g.previewImageUrl}):t.jsx("div",{className:"subscription-cover workshop-cover-empty judge-mod-cover",children:t.jsx(Ua,{size:24})}),t.jsxs("div",{className:"mod-card-copy",children:[t.jsxs("div",{className:"mod-card-title-row",children:[t.jsx("span",{className:"mod-type-badge judge",children:"Judge Mod"}),t.jsx("span",{className:"mod-source-badge workshop",children:"Workshop"}),t.jsx("strong",{children:g.title})]}),t.jsxs("span",{children:[g.author||"Unknown"," / ",g.imageCount," images / ",g.soundCount," sounds"]})]}),t.jsxs("div",{className:"saved-pack-actions",children:[t.jsxs("button",{className:`action-button ${S||k||C?"installing-button":F?"installed-button":""}`,type:"button","aria-pressed":F,disabled:k||C||ma,onClick:()=>U?F?void hm():void lm(U):void td(g),title:A?F?"Click again to return to default judges":"Enable Judge Mod":"Retry installation",children:[A?F?t.jsx(nt,{size:16}):t.jsx(_e,{size:16}):k||C?null:t.jsx(Gt,{size:16}),k||C?t.jsx(jn,{percent:k&&go.packId===g.id?go.percent:void 0,pendingLabel:S||R?"Installing...":"Waiting..."}):A?F?"In Use":"Enable":"Retry Install"]}),t.jsxs("button",{className:"ghost-button unsubscribe-mod-button",type:"button",disabled:os.has(g.id),onClick:()=>{Jb(g,!1)},title:"Unsubscribe and remove installed mod","aria-label":`Unsubscribe from ${g.title} and remove it`,children:[t.jsx(wc,{className:"unsubscribe-desktop-icon",size:16}),t.jsx(Ho,{className:"unsubscribe-mobile-icon",size:16}),t.jsx("span",{className:"unsubscribe-mod-label",children:"Unsubscribe"})]})]})]},`judge:${g.id}`)}),h.map(g=>{const S=ye===g.id,k=a&&cm(g),R=S&&H?Bo(H):g.summary,C=ew(g,R);return t.jsxs("article",{className:`mod-card saved-pack-card mod-kind-dub ${S?"active":""}`,children:[t.jsx(bt,{size:26}),t.jsxs("div",{className:"mod-card-copy",children:[t.jsxs("div",{className:"mod-card-title-row",children:[t.jsx("span",{className:"mod-type-badge dub",children:"Dub Mod"}),t.jsx("span",{className:`mod-source-badge ${k?"workshop":"local"}`,children:k?"Workshop":"Local"}),t.jsx("strong",{children:R?.title||g.name})]}),t.jsx("span",{children:C||"Saved locally"})]}),t.jsx("div",{className:"saved-pack-actions",children:k?t.jsx("button",{className:"ghost-button installing-button workshop-syncing-mod-button",type:"button",disabled:!0,"aria-label":"Syncing Workshop subscription",children:t.jsx(jn,{pendingLabel:"Syncing..."})}):t.jsx("button",{className:"danger-button icon-button",type:"button",onClick:()=>{Wb(g)},title:"Remove imported mod",children:t.jsx(Ho,{size:16})})})]},`local-dub:${g.id}`)}),x.map(g=>{const S=Dt?.id===g.id,k=a&&!!(g.workshopId||g.workshopSlug),R=ey(g);return t.jsxs("article",{className:`mod-card saved-pack-card workshop-subscription-card mod-kind-judge ${S?"active":""}`,children:[R?t.jsx("img",{alt:"",className:"subscription-cover judge-mod-cover",src:R}):t.jsx("div",{className:"subscription-cover workshop-cover-empty judge-mod-cover",children:t.jsx(Ua,{size:24})}),t.jsxs("div",{className:"mod-card-copy",children:[t.jsxs("div",{className:"mod-card-title-row",children:[t.jsx("span",{className:"mod-type-badge judge",children:"Judge Mod"}),t.jsx("span",{className:`mod-source-badge ${k?"workshop":"local"}`,children:k?"Workshop":"Local"}),t.jsx("strong",{children:g.title})]}),t.jsx("span",{children:ty(g)})]}),t.jsx("div",{className:"saved-pack-actions",children:k?t.jsx("button",{className:"ghost-button installing-button workshop-syncing-mod-button",type:"button",disabled:!0,"aria-label":"Syncing Workshop subscription",children:t.jsx(jn,{pendingLabel:"Syncing..."})}):t.jsxs(t.Fragment,{children:[t.jsxs("button",{className:`action-button ${S?"installed-button":""}`,type:"button","aria-pressed":S,disabled:ma,onClick:()=>S?void hm():void lm(g),title:S?"Click again to return to default judges":"Enable Judge Mod",children:[S?t.jsx(nt,{size:16}):t.jsx(_e,{size:16}),S?"In Use":"Enable"]}),t.jsx("button",{className:"danger-button icon-button",type:"button",disabled:ma,onClick:()=>{Qb(g)},title:"Remove imported mod",children:t.jsx(Ho,{size:16})})]})})]},`local-judge:${g.id}`)})]})}
159function nd(...a){const n=rs.trim().toLocaleLowerCase();return n?a.some(s=>s?.toLocaleLowerCase().includes(n)):!0}function nC(){return t.jsxs("div",{className:"empty-state compact-search-empty",children:[t.jsx(dd,{size:26}),t.jsx("strong",{children:r('No mods match "{query}"',{query:rs.trim()})}),t.jsx("button",{className:"ghost-button",type:"button",onClick:()=>Mu(""),children:r("Clear Search")})]})}function iC(){return Cu==="judge"?pa.length?t.jsx("div",{className:"saved-pack-list",children:pa.map(a=>{const n=Dt?.id===a.id,s=HP(a),d=ey(a),u=ty(a);return t.jsxs("article",{className:`mod-card saved-pack-card workshop-subscription-card ${n?"active":""}`,children:[d?t.jsx("img",{alt:"",className:"subscription-cover judge-mod-cover",src:d}):t.jsx("div",{className:"subscription-cover workshop-cover-empty judge-mod-cover",children:t.jsx(Ua,{size:24})}),t.jsxs("div",{children:[t.jsx("strong",{children:a.title}),t.jsx("span",{children:u})]}),t.jsxs("div",{className:"saved-pack-actions",children:[t.jsxs("button",{className:`action-button ${n?"installed-button":""}`,type:"button","aria-pressed":n,disabled:ma,onClick:()=>n?void hm():void lm(a),title:n?"Click again to return to default judges":"Enable Judge Mod",children:[n?t.jsx(nt,{size:16}):t.jsx(_e,{size:16}),n?"In Use":"Enable"]}),s?null:t.jsx("button",{className:"danger-button icon-button",type:"button",disabled:ma,onClick:()=>{Qb(a)},title:"Remove imported mod",children:t.jsx(Ho,{size:16})})]})]},a.id)})}):t.jsxs("div",{className:"empty-state",children:[t.jsx("strong",{children:"No judge mod installed"}),t.jsx("p",{children:"Import a Judge Mod ZIP to customize the score reveal."})]}):Le.length?t.jsx("div",{className:"saved-pack-list",children:Le.map(a=>{const n=ye===a.id,s=cm(a),d=n&&H?Bo(H):a.summary,u=ew(a,d);return t.jsxs("article",{className:`mod-card saved-pack-card ${n?"active":""}`,children:[t.jsx(bt,{size:26}),t.jsxs("div",{children:[t.jsx("strong",{children:d?.title||a.name}),t.jsx("span",{children:u||"Saved locally"})]}),t.jsxs("div",{className:"saved-pack-actions",children:[t.jsxs("button",{className:"action-button",type:"button",onClick:()=>{Bf(a)},title:"Play mod",children:[t.jsx(_e,{size:16}),"Play"]}),s?null:t.jsx("button",{className:"danger-button icon-button",type:"button",onClick:()=>{Wb(a)},title:"Remove imported mod",children:t.jsx(Ho,{size:16})})]})]},a.id)})}):t.jsxs("div",{className:"empty-state",children:[t.jsx("strong",{children:"No dub mod installed"}),t.jsx("p",{children:"Import a ZIP or folder to add one."})]})}function sC(){const a=v?.game;if(T!=="multi"||!a||Ge(a)!=="coop"||v?.hostPlayerId!==v?.playerId||ge)return null;const n=to(v),s=Pi(a),d=a.currentAssignmentIndex??0,u=d>0&&!(s==="claim"&&n?.playerId),h=s==="claim"&&!!n?.playerId&&n?.playerId!==v.playerId&&!ae,x=d>=a.assignments.length-1;return t.jsxs("div",{className:"room-emergency-menu",onBlur:g=>{g.currentTarget.contains(g.relatedTarget)||Zn(!1)},children:[t.jsxs("button",{className:"ghost-button icon-button room-emergency-trigger",type:"button","aria-expanded":mf,"aria-haspopup":"menu",onClick:()=>Zn(g=>!g),title:"Host emergency controls",children:[t.jsx(Py,{"aria-hidden":"true",size:16}),t.jsx("span",{children:"Host Tools"})]}),mf?t.jsxs("div",{className:"room-emergency-popover",role:"menu",children:[t.jsxs("div",{className:"room-emergency-heading",children:[t.jsx("strong",{children:"Host tools"}),t.jsx("span",{children:"Save the room progress or recover from a mistaken or stuck turn."})]}),s==="claim"?t.jsxs("button",{className:"room-emergency-export",type:"button",role:"menuitem",disabled:Yu||ae||ve,onClick:()=>{Zn(!1),JR()},title:ae?"Finish the current recording before exporting":"Save current progress as a resumable Recovery ZIP",children:[t.jsx(Gt,{size:18}),Yu?"Preparing ZIP...":"Export Recovery ZIP"]}):null,t.jsxs("button",{className:"room-emergency-redo",type:"button",role:"menuitem",disabled:!u||rl||ae||ve,onClick:()=>{Zn(!1),qR()},title:u?"Remove the latest take and reopen that line":"No completed line can be reopened yet",children:[t.jsx(it,{size:18}),rl?"Going back...":"Redo last line"]}),t.jsxs("button",{className:"room-emergency-skip",type:"button",role:"menuitem",disabled:_u||rl||ae||ve,onClick:()=>{Zn(!1),HR()}
159,title:x?"Skip this line and finish the room run":"Discard this line and move to the next one",children:[t.jsx(Ho,{size:18}),_u?"Skipping...":x?"Skip and finish":"Skip current line"]}),h?t.jsxs("button",{className:"room-emergency-release",type:"button",role:"menuitem",disabled:ln||ae||ve,onClick:()=>{Zn(!1),qp()},children:[t.jsx(Go,{size:18}),ln?"Releasing...":"Release stuck line"]}):null]}):null]})}function ay(){if(!q)return null;const a=!!q.referenceVideoUrl,n=T==="multi"&&!!v?.game,s=to(),d=Ut(),u=q.data.clip.caption||q.data.clip.title,h=Hk(q.speakerNames),x=_D(u),g=(re="")=>t.jsxs("div",{className:`single-stage-subtitle ${x} ${re}`.trim(),children:[h?t.jsx("span",{className:"stage-speaker-label",children:h}):null,t.jsx("p",{children:u})]}),S=Na(d),k=S?ko[S]:void 0,R=!!k?.playerAudio,C=!!(k?.playerAudio||k?.playerUrl||Oe||Ce),A=n&&am(v?.game)&&C&&!ae,F=ae?"recording":C?"review":"idle",$=ae?"recording":C?"review":"idle",Q=Ge(v?.game),J=Pi(v?.game),le=n?Q==="pvp"?v?.game?.clipCount??0:v?.game?.assignments.length??0:IP(),de=n?Q==="pvp"?s?.index??0:v?.game?.currentAssignmentIndex??0:Bb(),ie=n?Aa():!0,Pe=n?ql():!1,dt=n&&J==="claim"&&ie&&!ae&&!ge,Je=n?Q==="pvp"?de>=le-1:(v?.game?.currentAssignmentIndex??0)>=(v?.game?.assignments.length??1)-1:le>0&&de>=le-1,Fe=Si(),Ai=ae?jh(Sf,Fe.totalSeconds):k?.playerUrl||Oe?1:0,gr=ae?Ai:Fx,Ys=ae&&Sf<Xr,Kr=ae?r(Ys?"GET READY":"RECORDING"):"",Ii=Ys?"recording-cue preparing":"recording-cue active",Ft=!hs&&!ae&&!Oe&&!k?.playerUrl&&!!q.referenceAudioUrl,Wo=qe||Ft?gr:void 0,za=ks?gr:void 0,Oa=ae?void 0:k?.playerAudio?.samples,ao=ae?Bx:void 0,Zr=I("stageFrame");if(!n){const re=hs||ae||!!Oe||!q.referenceAudioUrl,Mt=ae?"":oL(zc),no=`talk-stage-frame stage-zoomed stage-playback-ready ${Zr?"has-stage-art":"line-stage-frame"}`;return t.jsx("section",{className:"single-performance-layout",children:t.jsxs("div",{className:"single-performance-stage",children:[t.jsx("div",{className:no,"aria-label":"Solo dubbing stage",children:t.jsxs("div",{className:"talk-stage-frame-inner",children:[Zr?t.jsx("img",{className:"talk-stage-art",src:Zr,alt:""}):null,t.jsx("div",{className:`talk-stage-screen performance-screen ${a?"has-video":""}`,children:a?t.jsx("video",{"data-round-id":q.data.roundId,muted:!0,playsInline:!0,preload:"auto",poster:q.referenceImageUrl,ref:fe,src:q.referenceVideoUrl,onEnded:mb,onLoadedMetadata:jt=>Sn(jt.currentTarget,q),onPlay:jt=>Xl(jt.currentTarget),onTimeUpdate:jt=>yb(jt.currentTarget)},`reference-video:${q.data.roundId}`):q.referenceImageUrl?t.jsx("img",{alt:"",src:q.referenceImageUrl}):t.jsx("div",{className:"stage-screen-placeholder",children:t.jsx(bt,{size:54})})}),g("stage-frame-subtitle")]})}),q.referenceAudioUrl?t.jsx("audio",{"data-round-id":q.data.roundId,className:"hidden-audio",preload:"auto",ref:Pt,src:q.referenceAudioUrl,onEnded:pb,onPlay:jt=>Xl(jt.currentTarget),onTimeUpdate:jt=>fb(jt.currentTarget)},`reference-audio:${q.data.roundId}`):null,Ce?t.jsx("audio",{"data-round-id":q.data.roundId,className:"hidden-audio",preload:"auto",ref:ir,src:Ce,onEnded:Db,onPlay:jt=>Ab(jt.currentTarget),onTimeUpdate:jt=>Nb(jt.currentTarget)},`take-audio:${q.data.roundId}:${Ce}`):null,H?.backingTrackUrl?t.jsx("audio",{className:"hidden-audio",preload:"auto",ref:ht,src:H.backingTrackUrl}):null,t.jsx("div",{className:`single-stage-control-deck ${re?"ready":"playing"}`,children:re?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"single-stage-waveform-strip",children:[t.jsxs("div",{className:"single-stage-status compact",children:[t.jsx("button",{className:"single-clip-picker-trigger",type:"button",disabled:ae||ve||de>=le-1,onClick:()=>{un(void 0),gs(!0)},title:r("Jump ahead to another clip"),children:r("CLIP {current} / {total}",{current:de+1,total:le||1})}),Kr?t.jsx("span",{className:Ii,children:Kr}):null]}),t.jsx(kd,{className:"single-stage-waveform",contentEndProgress:Fe.contentEndProgress,contentStartProgress:Fe.contentStartProgress,label:`Clip ${de+1} reference waveform`,liveLevels:ao,playerContentEndProgress:1,playerContentStartProgress:Fe.contentStartProgress,playerRevealProgress:za,playerSamples:Oa,progress:gr,revealProgress:Wo,samples:q.referenceAudio?.samples,showCaption:!1})]}),t.jsx("div",{className:`single-stage-actions solo-stage-actions ${$}`,children:$==="recording"?t.jsxs("button",{className:"danger-button",type:"button",onClick:()=>{Jp()},title:r("Stop Recording"),children:[t.jsx(pd,{size:18}),r("Stop Recording")]}):$==="review"?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"room-review-actions solo-review-actions",children:[t.jsx("button",{className:`ghost-button${qe?" is-playing":""}`,type:"button",disabled:!q.referenceAudioUrl||ve,onClick:()=>{ki()},"aria-label":qe?"Replay original clip, playing":void 0,"aria-pressed":qe,title:r("Replay original clip"),children:xi("Replay",qe)}),Lb(!Ce||ve),t.jsxs("button",{className:"ghost-button",type:"button",disabled:ve||!Oe&&!Ce,onClick:rd,title:"Clear this take and record again",children:[t.jsx(it,{size:18}),r("Retry")]})]}
159),t.jsxs("button",{className:"action-button next-turn-button",type:"button",disabled:ve||!Oe||ae||!dm(),onClick:()=>{ub()},title:r(Je?"Finish pack":"Go to next clip"),children:[t.jsx(_e,{size:18}),ve?"...":r(Je?"Finish":"Next")]})]}):t.jsxs(t.Fragment,{children:[t.jsx("button",{className:`ghost-button stage-replay-button${qe?" is-playing":""}`,type:"button",disabled:!q.referenceAudioUrl||ae||ve,onClick:()=>{ki()},"aria-label":qe?"Replay original clip, playing":void 0,"aria-pressed":qe,title:r("Replay original clip"),children:xi("Replay",qe)}),t.jsxs("div",{className:"stage-record-control",children:[t.jsxs("button",{className:"action-button stage-record-button",type:"button",disabled:!oy||ve,onClick:Gp,title:r(ta?"Reconnect microphone and record":"Record"),children:[t.jsx(fm,{size:18}),ry]}),t.jsx("button",{className:`recording-guide-toggle ${ar?"active":""}`,type:"button","aria-label":`${ar?"Disable":"Enable"} original guide while recording`,"aria-pressed":ar,disabled:ae||ut||ve||!q.referenceAudioUrl,onClick:bb,title:`${ar?"Original guide on":"Original guide off"}. Headphones recommended.`,children:t.jsx(bm,{size:15})})]})]})}),Mt?t.jsx("p",{className:"single-stage-notice",role:"status",children:Mt}):null]}):t.jsx("div",{className:"single-stage-prompt",children:t.jsxs("div",{className:"single-stage-waveform-strip",children:[t.jsx("div",{className:"single-stage-status compact",children:t.jsx("span",{children:r("CLIP {current} / {total}",{current:de+1,total:le||1})})}),t.jsx(kd,{className:"single-stage-waveform",contentEndProgress:Fe.contentEndProgress,contentStartProgress:Fe.contentStartProgress,label:`Clip ${de+1} reference waveform`,liveLevels:ao,playerContentEndProgress:1,playerContentStartProgress:Fe.contentStartProgress,playerRevealProgress:za,playerSamples:Oa,progress:gr,revealProgress:Wo,samples:q.referenceAudio?.samples,showCaption:!1})]})})})]})})}const Ks=hs||ae||!!Oe||!q.referenceAudioUrl,id=`talk-stage-frame stage-zoomed stage-playback-ready ${Zr?"has-stage-art":"line-stage-frame"}`,sd=Pe?"OPEN LINE":mr(v?.game)?"RACING":ie?"YOUR TURN":`${s?.playerName??"PLAYER"} TURN`,Zs=v?.players.find(re=>re.id===s?.playerId),De=Zs?.supporterIdentity,$o=v?.game?AP(v.game):EP(Q),fr=Q==="pvp"?eb():[],c
159d=fr.length>1?t.jsx("div",{className:"pvp-turn-flow","aria-label":"PVP turn order",children:fr.map((re,Mt)=>t.jsxs("div",{className:`pvp-turn-step ${re.status} ${re.isSelf?"self":""}`,children:[t.jsx("span",{className:"pvp-turn-marker",children:re.status==="done"?t.jsx(nt,{size:13}):Mt+1}),t.jsx("strong",{children:t.jsx(_o,{name:re.playerName??"Player",identity:v?.players.find(no=>no.id===re.playerId)?.supporterIdentity,nameColor:v?.players.find(no=>no.id===re.playerId)?.supporterNameColor})}),t.jsx("span",{children:re.status==="active"?re.isSelf?"You":"Now":re.status==="done"?"Done":re.status==="next"?re.isSelf?"You next":"Next":re.isSelf?"You later":"Waiting"})]},`${re.assignmentIndex}:${re.playerId}`))}):null,ld=s?.playerName??"Player",Xs=v?.game?.assignments[(v.game.currentAssignmentIndex??0)+1],Qs=v?.players.find(re=>re.id===Xs?.playerId),Mn=Xs?.playerName??Qs?.name,ue=Mn?t.jsxs(t.Fragment,{children:["Next: ",t.jsx(_o,{name:Mn,identity:Qs?.supporterIdentity,nameColor:Qs?.supporterNameColor})]}):J==="claim"&&Xs?"Next line: open claim":"Final turn";return t.jsx("section",{className:"single-performance-layout multiplayer-performance-layout",children:t.jsxs("div",{className:"single-performance-stage",children:[t.jsx("div",{className:id,"aria-label":"Multiplayer dubbing stage",children:t.jsxs("div",{className:"talk-stage-frame-inner",children:[Zr?t.jsx("img",{className:"talk-stage-art",src:Zr,alt:""}):null,t.jsx("div",{className:`talk-stage-screen performance-screen ${a?"has-video":""}`,children:a?t.jsx("video",{"data-round-id":q.data.roundId,muted:!0,playsInline:!0,preload:"auto",poster:q.referenceImageUrl,ref:fe,src:q.referenceVideoUrl,onEnded:mb,onLoadedMetadata:re=>Sn(re.currentTarget,q),onPlay:re=>Xl(re.currentTarget),onTimeUpdate:re=>yb(re.currentTarget)},`reference-video:${q.data.roundId}`):q.referenceImageUrl?t.jsx("img",{alt:"",src:q.referenceImageUrl}):t.jsx("div",{className:"stage-screen-placeholder",children:t.jsx(bt,{size:54})})}),g("stage-frame-subtitle")]})}),q.referenceAudioUrl?t.jsx("audio",{"data-round-id":q.data.roundId,className:"hidden-audio",preload:"auto",ref:Pt,src:q.referenceAudioUrl,onEnded:pb,onPlay:re=>Xl(re.currentTarget),onTimeUpdate:re=>fb(re.currentTarget)},`reference-audio:${q.data.roundId}`):null,t.jsx("div",{className:`single-stage-control-deck multiplayer-stage-control-deck ${Ks?"ready":"playing"}`,children:Ks?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"single-stage-flow-stack",children:[t.jsxs("div",{className:"single-stage-waveform-strip",children:[t.jsxs("div",{className:"single-stage-status compact multiplayer-turn-status",children:[t.jsxs("span",{children:["CLIP ",de+1," / ",le||1]}),t.jsx("span",{className:"turn-label",children:$o}),t.jsxs("span",{className:ie||Pe?"turn-label active":"turn-label",children:[sd,De?t.jsx(tu,{identity:De,compact:!0}):null]}),t.jsxs("span",{className:"room-stage-player-count",title:r("Players online"),children:[t.jsx(In,{size:13}),zm(v)," / ",v?.players.length??0]}),Kr?t.jsx("span",{className:Ii,children:Kr}):null]}),t.jsx(kd,{className:"single-stage-waveform",contentEndProgress:Fe.contentEndProgress,contentStartProgress:Fe.contentStartProgress,label:`Clip ${de+1} reference waveform`,liveLevels:ao,playerContentEndProgress:1,playerContentStartProgress:Fe.contentStartProgress,playerRevealProgress:za,playerSamples:Oa,progress:gr,revealProgress:Wo,samples:q.referenceAudio?.samples,showCaption:!1})]}),cd]}),Pe?t.jsxs("div",{className:"multiplayer-claim-actions","aria-live":"polite",children:[t.jsxs("div",{children:[t.jsx("strong",{children:"Open line"}),t.jsx("span",{children:"Any player can take this clip."})]}),t.jsxs("button",{className:"action-button",type:"button",disabled:Ju||ve||ae,onClick:()=>{GR()},title:"Take this clip",children:[t.jsx(fm,{size:18}),Ju?"Taking...":"Take Line"]})]}):ie?t.jsx("div",{className:`single-stage-actions multiplayer-stage-actions ${F} ${A?"allow-retakes":""} ${dt?"allow-release":""}`,children:F==="recording"?t.jsxs("button",{className:"danger-button",type:"button",disabled:ve,onClick:()=>{Jp()},title:"Stop recording",children:[t.jsx(pd,{size:18}),"Stop Recording"]}):F==="review"?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"room-review-actions",children:[XR(),t.jsx("button",{className:`ghost-button${qe?" is-playing":""}`,type:"button",disabled:!q.referenceAudioUrl||ve,onClick:()=>{ki()},"aria-label":qe?"Replay original clip, playing":void 0,"aria-pressed":qe,title:"Replay original clip",children:xi("Replay",qe)}),Lb(!Ce||ve),A?t.jsxs("button",{className:"ghost-button",type:"button",disabled:!_P(),onClick:rd,title:"Clear this take and record again",children:[t.jsx(it,{size:18}),"Retry"]}):null,dt?t.jsxs("button",{className:"ghost-button",type:"button",disabled:ln||ve,onClick:()=>{qp()},title:"Release this line",children:[t.jsx(Go,{size:18}),ln?"...":"Release"]}):null]}
159),t.jsxs("button",{className:"action-button next-turn-button",type:"button",disabled:ve||!R,onClick:()=>{ub()},title:Je?"Finish room run":"Next turn",children:[t.jsx(_e,{size:18}),ve?"...":Je?"Finish":"Next"]})]}):t.jsxs(t.Fragment,{children:[t.jsx("button",{className:`ghost-button stage-replay-button${qe?" is-playing":""}`,type:"button",disabled:!q.referenceAudioUrl||ve,onClick:()=>{ki()},"aria-label":qe?"Replay original clip, playing":void 0,"aria-pressed":qe,title:"Replay original clip",children:xi("Replay",qe)}),dt?t.jsxs("button",{className:"ghost-button",type:"button",disabled:ln||ve,onClick:()=>{qp()},title:"Release this line",children:[t.jsx(Go,{size:18}),ln?"...":"Release"]}):null,t.jsxs("div",{className:"stage-record-control",children:[t.jsxs("button",{className:"action-button stage-record-button",type:"button",disabled:!oy||ve,onClick:Gp,title:ta?"Reconnect microphone and record":"Record",children:[t.jsx(fm,{size:18}),ry]}),Ge(v?.game)==="coop"?t.jsx("button",{className:`recording-guide-toggle ${ar?"active":""}`,type:"button","aria-label":`${ar?"Disable":"Enable"} original guide while recording`,"aria-pressed":ar,disabled:ae||ut||ve||!q.referenceAudioUrl,onClick:bb,title:`${ar?"Original guide on":"Original guide off"}. Headphones recommended.`,children:t.jsx(bm,{size:15})}):null]})]})}):t.jsxs("div",{className:"multiplayer-waiting-actions","aria-live":"polite",children:[t.jsxs("div",{children:[t.jsxs("strong",{children:[t.jsx(_o,{name:ld,identity:De,nameColor:Zs?.supporterNameColor})," is up"]}),t.jsx("span",{children:ue})]}),t.jsxs("div",{className:"room-waiting-controls",children:[t.jsx("button",{className:`ghost-button${qe?" is-playing":""}`,type:"button",disabled:!q.referenceAudioUrl||ae||ve,onClick:()=>{ki()},"aria-label":qe?"Replay original clip, playing":void 0,"aria-pressed":qe,title:"Replay original clip",children:xi("Replay",qe)}),ZR()]})]})]}):t.jsx("div",{className:"single-stage-prompt",children:t.jsxs("div",{className:"single-stage-flow-stack",children:[t.jsxs("div",{className:"single-stage-waveform-strip",children:[t.jsxs("div",{className:"single-stage-status compact multiplayer-turn-status",children:[t.jsxs("span",{children:["CLIP ",de+1," / ",le||1]}),t.jsx("span",{className:"turn-label",children:$o}),t.jsxs("span",{className:ie||Pe?"turn-label active":"turn-label",children:[sd,De?t.jsx(tu,{identity:De,compact:!0}):null]}),t.jsxs("span",{className:"room-stage-player-count",title:r("Players online"),children:[t.jsx(In,{size:13}),zm(v)," / ",v?.players.length??0]})]}),t.jsx(kd,{className:"single-stage-waveform",contentEndProgress:Fe.contentEndProgress,contentStartProgress:Fe.contentStartProgress,label:`Clip ${de+1} reference waveform`,liveLevels:ao,playerContentEndProgress:1,playerContentStartProgress:Fe.contentStartProgress,playerRevealProgress:za,playerSamples:Oa,progress:gr,revealProgress:Wo,samples:q.referenceAudio?.samples,showCaption:!1})]}),cd]})})}),Ce?t.jsx("audio",{"data-round-id":q.data.roundId,className:"hidden-audio",preload:"auto",ref:ir,src:Ce,onEnded:Db,onPlay:re=>Ab(re.currentTarget),onTimeUpdate:re=>Nb(re.currentTarget)},`take-audio:${q.data.roundId}:${Ce}`):null,H?.backingTrackUrl?t.jsx("audio",{className:"hidden-audio",preload:"auto",ref:ht,src:H.backingTrackUrl}):null]})})}function ny(){const a=Math.max(0,Math.min(1,Number.isFinite(Io)?Io:0)),n={"--recording-progress":a},s=wr==="shorts"?"MY SHORTS VIDEO":"MY DUBBED WORK",d=Zo==="loading"?`Loading source video${Ki>1?` · retry ${Ki}/3`:""}`:Zo==="finalizing"?"Finalizing download":"Rendering video â keep this page open";return t.jsxs("div",{className:"recording-progress-panel",children:[t.jsxs("div",{className:"recording-progress-heading",children:[t.jsx("span",{children:s}),t.jsx("strong",{children:Zo==="loading"?"Startingâ¦":Zo==="finalizing"?"Finishingâ¦":`${Math.round(a*100)}%`})]}),t.jsx("div",{className:`recording-progress-track ${Zo==="loading"?"indeterminate":""}`,"aria-hidden":"true",style:n,children:t.jsx("div",{})}),t.jsxs("div",{className:"recording-progress-status",children:[t.jsx("span",{children:d}),wr?t.jsx("button",{type:"button",onClick:()=>
159yP(wr),children:ia?"Cancel & Retry":"Cancel Background"}):null]})]})}function iy(a){if(!ho||wr!==a)return null;const n=Math.max(0,Math.min(1,Number.isFinite(Io)?Io:0)),s={"--download-progress":n};return t.jsx("span",{className:`download-progress-ring ${Zo==="loading"?"indeterminate":""}`,style:s,"aria-label":Zo==="loading"?"Loading source video":`${Math.round(n*100)}% rendered`,children:t.jsx("span",{children:Zo==="loading"?"â¦":`${Math.round(n*100)}%`})})}function um(a){return[...Yt,...pt].find(n=>Yk(n,a))}function cC(a){const n=Di(a);if(n)return ZS[Li(n)]??um(n)?.userRating}function lC(a){const n=Di(a);if(!n)return;const s=Li(n),d=um(n);return QS[s]??(d?{ratingCount:d.ratingCount,ratingAverage:d.ratingAverage}:void 0)}function dC(a){if(!ee||!a)return null;const n=Di(a);if(!n)return null;const s=Li(n),d=cC(a),u=lC(a),h=!!tx[s],x=xg===s;return t.jsxs("section",{className:"result-action-section result-pack-rating","aria-label":"Rate dub pack",children:[t.jsxs("div",{className:"result-action-section-header",children:[t.jsx("span",{children:"Dub Pack Rating"}),d?t.jsxs("strong",{className:"result-pack-rating-saved",children:[t.jsx(nt,{size:14}),"Rated ",d,"/5"]}):x?t.jsxs("strong",{className:"result-pack-rating-saving",children:[t.jsx("span",{className:"result-pack-rating-spinner","aria-hidden":"true"}),"Saving"]}):null]}),t.jsx("p",{className:"result-pack-rating-total",children:ZD(u,h)}),d?t.jsx("p",{children:"Thanks for rating this pack."}):t.jsxs(t.Fragment,{children:[t.jsx("div",{className:"result-pack-rating-buttons",children:[1,2,3,4,5].map(g=>t.jsx("button",{className:x?"pending":"",disabled:x,type:"button",onClick:()=>{LP(a,g)},title:`Rate ${g} out of 5`,children:g},g))}),Pg?t.jsx("p",{className:"result-pack-rating-error",children:Pg}):null]})]})}function sy(a,n,s){const d=ho&&wr==="dub",u=ho&&wr==="shorts",h=!!(n&&s>0&&a),x=yt(),g=!!(a&&(Xo?ci.current[a.id]:zr.current[a.id])),S=!!(a&&(Xo?bn.current[a.id]:Ca.current[a.id])),k=g?"Ready":u?"Rendering":ho?"Waiting":"Not rendered",R=S?"Ready":d?"Rendering":ho?"Waiting":"Not rendered",C=!!(a&&Pl.current[a.id]);return t.jsxs("section",{className:"result-action-section result-video-download-panel","aria-label":r("Download exports"),children:[t.jsxs("div",{className:"result-action-section-header",children:[t.jsx("span",{children:r("Download My Work")}),t.jsxs("div",{className:"export-subtitle-toggle","aria-label":r("Video subtitle option"),children:[t.jsx("button",{"aria-pressed":!Xo,className:Xo?"":"active",disabled:ho,type:"button",onClick:()=>Zi(!1),title:"Export video without burned-in subtitles",children:r("Clean")}),t.jsx("button",{"aria-pressed":Xo,className:Xo?"active":"",disabled:ho,type:"button",onClick:()=>Zi(!0),title:"Export video with burned-in subtitles",children:r("Subtitles")})]})]}),t.jsxs("div",{className:"button-row result-download-actions",children:[t.jsxs("button",{className:`${g?"action-button ready-download-button":"ghost-button"} ${u?"rendering-download-button":k==="Waiting"?"waiting-download-button":k==="Not rendered"?"unrendered-download-button":""}`,type:"button",disabled:!g&&(!h||ia),onClick:()=>{wb()},title:k==="Waiting"?r("Waiting â click to switch rendering"):`${r("Download my vertical Shorts video")} · ${r(k)}`,children:[t.jsx(Gt,{size:18}),t.jsxs("span",{className:"result-download-label",children:[t.jsx("span",{children:r(x&&g?"Save Shorts":"My Shorts")}),t.jsx("span",{children:r("Video")})]}),u?iy("shorts"):h?t.jsx("span",{className:"result-download-state",children:r(k)}):null]}),t.jsxs("button",{className:`${S?"action-button ready-download-button":"ghost-button"} ${d?"rendering-download-button":R==="Waiting"?"waiting-download-button":R==="Not rendered"?"unrendered-download-button":""}`,type:"button",disabled:!S&&(!h||ia),onClick:()=>{kb()},title:R==="Waiting"?r("Waiting â click to switch rendering"):`${r("Download my landscape video")} · ${r(R)}`,children:[t.jsx(Gt,{size:18}),t.jsxs("span",{className:"result-download-label",children:[t.jsx("span",{children:r(x&&S?"Save Landscape":"My Landscape")}),t.jsx("span",{children:r("Video")})]}),d?iy("dub"):h?t.jsx("span",{className:"result-download-state",children:r(R)}):null]}),t.jsxs("button",{className:"ghost-button",type:"button",disabled:!a,onClick:()=>{vP(a)}
159,title:r("Download my audio track"),children:[t.jsx(Gt,{size:18}),t.jsxs("span",{className:"result-download-label",children:[t.jsx("span",{children:r(x&&C?"Save Audio":"My Audio")}),t.jsx("span",{children:r("Track")})]})]})]}),V?t.jsxs("div",{className:"video-export-playback-prompt",role:"status",children:[t.jsx("span",{children:r("Your browser needs one more tap to play this mod video.")}),t.jsxs("button",{className:"action-button",type:"button",onClick:()=>Ms.current?.continuePlayback(),children:[t.jsx(_e,{size:14}),r("Continue Export")]})]}):null,ca&&a?.id===ca.workId?t.jsxs("div",{className:"video-export-failure",role:"alert",children:[t.jsx("p",{children:r(ca.summary)}),t.jsxs("div",{className:"video-export-failure-actions",children:[t.jsxs("button",{className:"ghost-button",type:"button",disabled:ho||ia,onClick:()=>{dP()},children:[t.jsx(it,{size:14}),r("Compatibility Retry")]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{uP()},children:[t.jsx(Ga,{size:14}),r("Copy Details")]})]})]}):null,t.jsx("p",{className:"download-format-note",children:r(x?"Keep this page open. When ready, tap Save and choose Save Video or Save to Files.":"Exports are WebM. Chrome is recommended for playback; if you need MP4, please convert the file yourself.")})]})}function cy(a){const n=T==="multi"&&v?.game&&(v.game.workshopPackId||v.game.workshopPackSlug)?{id:v.game.workshopPackId,slug:v.game.workshopPackSlug}:void 0,s=T==="multi"?n:Di(a);return s?t.jsx(mj,{packTitle:n?v?.game?.packTitle??"Dub Mod":a?.packTitle??"Dub Mod",packId:s.id,packSlug:s.slug},Li(s)):null}function ly(a,n,s=!1){const d=a?"":" disabled-link",u=t.jsx("section",{className:"result-action-section result-share-section","aria-label":r("Share exports"),children:t.jsxs("div",{className:"result-share-toolbar",children:[t.jsx("span",{className:"result-share-mark","aria-hidden":"true",children:t.jsx(QC,{size:19})}),t.jsxs("div",{className:"button-row result-share-actions",children:[t.jsx("a",{"aria-label":"Upload on TikTok","aria-disabled":!a,className:`ghost-button platform-share-button${d}`,href:NN,onClick:h=>{a||h.preventDefault()},rel:"noopener noreferrer",target:"_blank",title:"Upload on TikTok",children:t.jsx("img",{src:"/tiktok-icon.svg",alt:"","aria-hidden":"true"})}),t.jsx("a",{"aria-label":"Upload on YouTube","aria-disabled":!a,className:`ghost-button platform-share-button${d}`,href:DN,onClick:h=>{a||h.preventDefault()},rel:"noopener noreferrer",target:"_blank",title:"Upload on YouTube",children:t.jsx("img",{src:"/youtube-icon.svg",alt:"","aria-hidden":"true"})})]})]})});return t.jsxs("div",{className:`result-action-groups${s?" pack-result-action-groups":""}`,children:[s?null:u,dC(a),s?null:cy(a),s?t.jsxs("div",{className:"result-footer-actions",children:[n?t.jsx("div",{className:"button-row result-room-actions",children:n}):null,u]}):n?t.jsx("div",{className:"button-row result-room-actions",children:n}):null]})}function dy(a,n,s){const d=a&&ne?.workId===a.id?ne.url:void 0,u=d??n?.videoUrl,h=!!(a&&n&&s>0);return t.jsx("section",{className:"dub-premiere-screen",children:t.jsxs("article",{className:"panel dub-premiere-panel",children:[t.jsx("div",{className:"panel-header dub-premiere-header",children:Ex?t.jsx("button",{className:"ghost-button",type:"button",onClick:lP,title:r("Skip"),children:r("Skip")}):t.jsx("span",{"aria-hidden":"true"})}),t.jsx("div",{className:"panel-body dub-premiere-body",children:t.jsx("div",{className:"clip-visual video-visual result-video-visual dub-premiere-visual",onClick:x=>{x.preventDefault(),!Nt&&h&&sP()},title:h?r("Play my dubbed work"):void 0,children:u&&n?t.jsx("video",{playsInline:!0,poster:n.imageUrl,ref:fe,src:u,muted:!d,onCanPlay:()=>P("Dub premiere ready."),onError:()=>P("This browser cannot play the pack video. Try converting the OGV to MP4/WebM."),onPlay:iP,onPlaying:()=>{d&&Me(!0)},onPause:Mb,onEnded:cP}):t.jsx("div",{className:"dub-premiere-empty",children:t.jsx("strong",{children:r("No playable dub video is available.")})})})})]})})}function uy(){const a=ro(),n=a?.clips??Fb(),s=a?.score??Nd(ko,n),d=n.find(g=>g.videoUrl),u=a?.completedCount??n.filter(g=>ko[g.id]?.score).length,h=a?.completedCount??n.filter(g=>ko[g.id]?.playerAudio||ko[g.id]?.playerUrl).length,x=py();return s&&!yo&&!Rt?dy(a,d,h):s&&!yo&&!ms?t.jsx("section",{className:"judge-results-screen",children:t.jsx(Tm,{score:s,judgePack:Dt,prepare:Ns,onComplete:()=>Nr(!0),staticPreview:p})}):t.jsxs("section",{className:`pack-results-layout${yo?" result-detail-layout":""}`,children:[t.jsx("div",{className:`panel result-summary-panel${yo?" result-detail-panel":""}`,children:yo?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"panel-header result-detail-header",children:[t.jsxs("button",{className:"mini-button",type:"button",onClick:()=>Ir(!1),title:r("Back to final score"),children:[t.jsx(Go,{size:14}),r("Back")]}),t.jsxs("div",{children:[t.jsx("h2",{children:r("Clip Scores")}),t.jsx("p",{children:r("{completed} / {total} clips completed",{completed:u,total:n.length})})]})]}),t.jsx("div",{className:"panel-body result-detail-body",children:t.jsx("div",{className:"result-clip-list",children:n.map((g,S)=>{const k=a?.takes[g.id]??ko[g.id];return t.jsxs("div",{className:"result-clip-row",children:[t.jsx("span",{children:String(S+1).padStart(2,"0")}),t.jsx("strong",{children:g.title}),t.jsx("b",{children:k?.score?k.score.total:"--"})]},g.id)})})})]}):t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:"panel-header result-summary-header",children:[t.jsxs("div",{className:"result-summary-heading",children:[t.jsx("h2",{children:r("Session complete")}
159),t.jsx("p",{children:r("{completed} / {total} clips completed",{completed:u,total:n.length})})]}),cy(a)]}),t.jsxs("div",{className:"panel-body control-stack result-summary-body",children:[t.jsxs("button",{className:"mini-button result-detail-button",type:"button",onClick:()=>Ir(!0),title:r("Show clip scores"),children:[t.jsx(Ua,{size:14}),r("Detail")]}),t.jsx(Pv,{score:s,judgePack:Dt,showNotes:!1}),ho?ny():null,sy(a,d,h),ly(a,x,!0)]})]})}),H?.backingTrackUrl?t.jsx("audio",{className:"hidden-audio",ref:ht,src:H.backingTrackUrl}):null]})}function uC(){const a=eD[Du];return Gg==="judging"?t.jsx("main",{className:"preview-shell preview-play-shell",children:t.jsx("section",{className:"judge-results-screen",children:t.jsx(Tm,{score:a,judgePack:Dt,prepare:Ns,onComplete:()=>{Nr(!0),Lu("result")}})})}):Gg==="result"?t.jsx("main",{className:"preview-shell preview-play-shell",children:pC(a)}):gx==="mods"?t.jsxs("main",{className:"app-shell compact-shell preview-setup-view",children:[t.jsx(Co,{active:"MODS",onMenu:()=>ns("menu"),showAuth:!1,showNav:!1}),t.jsxs("div",{className:"screen-toolbar",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>ns("menu"),title:"Back",children:[t.jsx(Go,{size:18}),"Menu"]}),t.jsxs("div",{className:"screen-title",children:[t.jsx("span",{children:"CREATOR PREVIEW"}),t.jsx("strong",{children:"Mods"})]}),t.jsx("span",{className:"toolbar-spacer","aria-hidden":"true"})]}),tC(),t.jsx(qo,{})]}):t.jsxs("main",{className:"menu-shell preview-menu-shell",children:[t.jsx(Co,{active:"PLAY",onMenu:()=>ns("menu"),showAuth:!1,showNav:!1}),t.jsx("section",{className:"menu-playfield",children:t.jsx("div",{className:"menu-stage",children:t.jsxs("div",{className:"menu-actions","aria-label":"Creator preview menu",children:[t.jsxs("button",{className:"menu-button",type:"button",onClick:()=>{mm("single")},onMouseEnter:Ta,children:[t.jsx(Ry,{size:30}),t.jsx("span",{children:"Single Player"})]}),t.jsxs("button",{className:"menu-button",type:"button",onClick:()=>{mm("multi")},onMouseEnter:Ta,children:[t.jsx(In,{size:30}),t.jsx("span",{children:"Multiplayer"})]}),t.jsxs("button",{className:"menu-button",type:"button",onClick:()=>ns("mods"),onMouseEnter:Ta,children:[t.jsx(bt,{size:30}),t.jsx("span",{children:"Mods"})]})]})})}),t.jsx(qo,{})]})}function pC(a){const n=I("stageFrame"),s=Du==="single"?"Single Player Preview":"Multiplayer Preview";return t.jsxs("section",{className:"pack-results-layout preview-results-layout",children:[t.jsxs("div",{className:"panel result-summary-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:"PREVIEW RESULT"}),t.jsx("h2",{children:s}),t.jsx("p",{children:"Score screen and result frame preview"})]}),t.jsxs("div",{className:"panel-body control-stack result-summary-body",children:[t.jsx(Pv,{score:a,judgePack:Dt,showNotes:!1}),t.jsxs("div",{className:"button-row preview-result-actions",children:[t.jsxs("button",{className:"action-button",type:"button",onClick:()=>{mm(Du)},children:[t.jsx(it,{size:18}),"Replay"]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:TC,children:[t.jsx(Go,{size:18}),"Back to Preview"]})]})]})]}),t.jsx("div",{className:"panel result-video-panel preview-result-visual-panel",children:t.jsxs("div",{className:`preview-result-visual ${n?"has-stage-art":"line-stage-frame"}`,children:[n?t.jsx("img",{alt:"",src:n}):null,t.jsxs("div",{children:[t.jsx("span",{children:z?z.title:"Default Theme"}),t.jsx("strong",{children:Dt?Dt.title:"Default Judges"})]})]})})]})}function py(){const a=v;if(T!=="multi"||!a||!Da(a.game))return null;const n=a.hostPlayerId===a.playerId,s=my(a);return n?t.jsxs(t.Fragment,{children:[s,t.jsxs("button",{className:"action-button",type:"button",disabled:Ku,onClick:()=>{jR()},title:"Return to room",children:[t.jsx(Go,{size:18}),Ku?"Returning...":"Back to Room"]})]}):t.jsxs(t.Fragment,{children:[s,t.jsx("span",{className:"room-results-lobby-note",children:"Waiting for host to return to room"})]})}function my(a=v){if(!a?.resultDeadlineAt)return null;const n=Date.parse(a.resultDeadlineAt)-Date.now(),s=900*1e3,d=a.status==="lobby"&&a.players.some(h=>!h.isHost&&h.isReady);if(n<=0||n>s||d)return null;const u=Math.max(1,Math.ceil(n/6e4));return t.jsxs("div",{className:"room-result-expiry-warning",role:"status",children:[t.jsx(rh,{size:16}),t.jsxs("span",{children:[t.jsxs("strong",{children:["Room closes in about ",u," min."]}),"No players are ready. Start another game to keep the room."]}
159)]})}function mC(){const a=v?.lobbyDeadlineAt,n=v&&a?`${v.roomCode}:${a}`:void 0;if(!n||Rx!==n)return null;const s=()=>{Xg.current.add(n),ls(void 0)};return t.jsx("div",{className:"room-lobby-warning-backdrop",role:"presentation",onClick:s,children:t.jsxs("section",{className:"room-lobby-warning-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"room-lobby-warning-title","aria-describedby":"room-lobby-warning-description",onClick:d=>d.stopPropagation(),children:[t.jsxs("div",{className:"room-lobby-warning-heading",children:[t.jsx(rh,{size:24,"aria-hidden":"true"}),t.jsxs("div",{children:[t.jsx("span",{children:r("ROOM CLOSING SOON")}),t.jsx("h2",{id:"room-lobby-warning-title",children:r(v?.visibility==="public"?"This public room has been waiting for 50 minutes.":"This room has been waiting for 10 minutes.")})]})]}),t.jsx("p",{id:"room-lobby-warning-description",children:r("Start the game within the next 10 minutes or the room will close automatically.")}),t.jsx("div",{className:"room-lobby-warning-actions",children:t.jsx("button",{className:"action-button",type:"button",onClick:s,children:r("Dismiss")})})]})})}function hC(){const a=v?.game;if(!a)return null;const n=NA(a,v?.players??[]),s=mr(a),d=n[0],u=Gs(),h=u?.clips??H?.clips??[],x=h.find(R=>R.videoUrl),g=u?.completedCount??0,S=py(),k=u?.score;return k&&!Rt?dy(u,x,g):k&&!ms?t.jsx("section",{className:"judge-results-screen",children:t.jsx(Tm,{score:k,judgePack:Dt,prepare:Ns,onComplete:()=>Nr(!0)})}):t.jsxs("section",{className:"room-results-layout pvp-results-layout",children:[t.jsxs("article",{className:"panel room-results-hero",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:"PVP RESULTS"}),t.jsx("h2",{children:d?t.jsxs(t.Fragment,{children:[t.jsx(_o,{name:d.playerName,identity:v.players.find(R=>R.id===d.playerId)?.supporterIdentity,nameColor:v.players.find(R=>R.id===d.playerId)?.supporterNameColor})," Wins"]}):"Results"}),t.jsx("p",{children:a.packTitle})]}),t.jsxs("div",{className:"panel-body",children:[t.jsx("div",{className:"pvp-podium",children:n.slice(0,3).map((R,C)=>t.jsxs("div",{className:`pvp-podium-card rank-${C+1}`,children:[t.jsx("span",{children:C+1}),t.jsx("strong",{children:t.jsx(_o,{name:R.playerName,identity:v.players.find(U=>U.id===R.playerId)?.supporterIdentity,nameColor:v.players.find(U=>U.id===R.playerId)?.supporterNameColor})}),t.jsx("b",{children:Math.round(R.battleScore)}),s?t.jsx("small",{children:tk(R.durationMs)}):null]},R.playerId))}),S?t.jsx("div",{className:"button-row room-results-actions",children:S}):null]})]}),t.jsxs("article",{className:"panel room-results-table-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:"SCOREBOARD"}),t.jsx("h2",{children:"Final Ranking"})]}),t.jsx("div",{className:"panel-body room-results-table",children:n.map((R,C)=>t.jsxs("div",{className:"room-score-row",children:[t.jsx("span",{children:String(C+1).padStart(2,"0")}),t.jsx("strong",{children:t.jsx(_o,{name:R.playerName,identity:v.players.find(U=>U.id===R.playerId)?.supporterIdentity,nameColor:v.players.find(U=>U.id===R.playerId)?.supporterNameColor})}),t.jsx("small",{children:s?tk(R.durationMs):`${R.takeCount} takes`}),t.jsx("b",{children:Math.round(R.battleScore)})]},R.playerId))})]}),t.jsxs("article",{className:"panel pvp-dub-work-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:"MY DUB"}),t.jsx("h2",{children:"Downloads"}),t.jsxs("p",{children:[g," / ",h.length||a.clipCount," takes recorded"]})]}),t.jsxs("div",{className:"panel-body pvp-dub-work-body",children:[ho?ny():null,sy(u,x,g),u?null:t.jsx("p",{className:"pvp-dub-empty",children:"No local dub playback is available for this browser."}),t.jsx("div",{className:"pvp-dub-work-actions",children:ly(u)})]}),u?.backingTrackUrl?t.jsx("audio",{className:"hidden-audio",ref:ht,src:u.backingTrackUrl}):null]})]})}function gC(){if(v?.roleSelectionActive)return t.jsx(oT,{packTitle:io(v.lobbyPack?.packTitle??"Dub Mod"),roles:v.lobbyPack?.characters??[],claims:v.roleClaims??[],currentPlayerId:v.playerId,isHost:v.hostPlayerId===v.playerId,pendingRole:sf,startDisabledReason:v.hostPlayerId===v.playerId?Gl(v):void 0,starting:ya,chatPanel:t.jsx(uT,{connected:Er,error:Bu,messages:Fu,players:v.players,onSend:Op}),roleLineCounts:xR(),onRoleChange:(n,s)=>{IR(n,s)},onStart:()=>{Yf()},onBack:()=>{RR()}});const a=LR();if(v?.game){if(v.status==="playing"&&mr(v.game)&&!v.game.completedAt&&Fs(v)===void 0){const u=v.players.filter(h=>v.game.assignments.map((x,g)=>({assignment:x,assignmentIndex:g})).filter(({assignment:x})=>x.playerId===h.id).every(({assignmentIndex:x})=>v.game.takes.some(g=>g.assignmentIndex===x))).length;return t.jsxs("section",{className:"panel waiting-stage","aria-live":"polite",children:[t.jsx("strong",{children:"You finished the race"}),t.jsxs("p",{children:["Waiting for the other players. ",u," / ",v.players.length," racers finished."]}),t.jsx("div",{className:"preparing-stage-bar","aria-hidden":"true"})]})}const n=Date.parse(v.game.startedAt),s=Number.isNaN(n)||Date.now()-n<15e3;if(gf||ya||!q&&v.status==="playing"&&s){const u=v.game.currentAssignmentIndex>
1590?"Loading the next clip.":"Loading the opening.";return t.jsxs("section",{className:"panel waiting-stage preparing-stage","aria-live":"polite",children:[t.jsx("strong",{children:"Preparing stage"}),t.jsx("p",{children:u}),t.jsx("div",{className:"preparing-stage-bar","aria-hidden":"true"})]})}return t.jsxs("section",{className:"panel waiting-stage",children:[t.jsx("strong",{children:"Syncing room"}),t.jsx("p",{children:"Reconnect to the room, or leave and return to multiplayer."}),t.jsxs("div",{className:"button-row waiting-stage-actions",children:[t.jsxs("button",{className:"ghost-button",type:"button",disabled:Ar,onClick:()=>{zp()},children:[Ar?t.jsx("span",{className:"inline-button-spinner","aria-hidden":"true"}):t.jsx(it,{size:18}),Ar?"Reconnecting...":"Reconnect"]}),t.jsxs("button",{className:"danger-button",type:"button",onClick:()=>{Wl()},children:[t.jsx(wc,{size:18}),"Leave Room"]})]})]})}return v?t.jsx(eT,{copiedTarget:Mx,countdown:jx,currentPlayerId:v.playerId,currentPlayerReady:!!a?.isReady,isHost:Js,maxPlayers:Math.max(v.players.length,v.maxPlayers??Td),players:v.players,roomCode:v.roomCode,recoveryMessage:v.recovery?`${v.recovery.completedTakes} recordings restored · Continue at line ${v.recovery.currentAssignmentIndex+1} of ${v.recovery.assignmentCount}. Mod and mode are kept from your backup.`:void 0,startDisabledReason:Js?Gl(v):void 0,startReadyMessage:Wp(v),startLabel:mt==="coop"&&ba==="role"&&!v.recovery?"Choose Roles":void 0,starting:ya,installingRoomPack:!!Pu,roomPackInstallPercent:Pu&&go.packId===Pu?go.percent:void 0,chatPanel:t.jsx(nS,{connected:Er,error:Bu,hostStartDeadlineAt:v.hostStartDeadlineAt,messages:Fu,players:v.players,notice:Js&&Gl(v)||Wp(v),onSend:Op}),onCopyCode:hR,onCopyInviteLink:gR,onKickPlayer:TR,onReportPlayer:ER,reportingEnabled:v.visibility==="public",onReadyToggle:()=>{CR(!a?.isReady)},onStartGame:()=>{Yf()}}):t.jsxs("section",{className:"panel waiting-stage",children:[t.jsx("strong",{children:"Lobby Ready"}),t.jsx("p",{children:v?"The host chooses a mod and starts the room.":"Create or join a room."})]})}function fC(){if(!or)return null;const a=or==="create"?"Creating Room":"Joining Room",n=or==="create"?"Preparing your invite room.":"Connecting to the invite room.",s=or==="join"&&!!pc();return t.jsx("div",{className:"room-loading-backdrop",role:"status","aria-live":"polite",children:t.jsxs("div",{className:"room-loading-card",children:[t.jsx("span",{className:"room-loading-spinner","aria-hidden":"true"}),t.jsx("strong",{children:a}),t.jsx("p",{children:n}),s?t.jsxs("button",{className:"ghost-button",type:"button",disabled:Ar,onClick:()=>{zp()},children:[t.jsx(it,{size:18}),Ar?"Reconnecting...":"Reconnect now"]}):null]})})}function bC(){if(!Qg||!v)return null;const a=Qg==="server-update";return t.jsx("div",{className:"room-loading-backdrop room-reconnect-backdrop",role:"status","aria-live":"assertive",children:t.jsxs("div",{className:`room-loading-card room-reconnect-card ${a?"server-update":"connection"}`,children:[t.jsx("span",{className:"room-loading-spinner","aria-hidden":"true"}),t.jsx("strong",{children:r(a?"Game server updated":"Connection interrupted")}),t.jsx("p",{children:r(a?"Syncing the latest game server and restoring your room.":"Your connection dropped. Trying to reconnect to your room.")})]})})}function yC(){if(!St||T!=="multi")return null;const a=Tx||Gc===St.workshopPack?.id,n=St.workshopPack&&go.packId===St.workshopPack.id?go.percent:void 0;return t.jsx("div",{className:"room-pack-sync-backdrop",role:"presentation",children:t.jsxs("section",{className:"panel room-pack-sync-modal",role:"dialog","aria-modal":"true","aria-labelledby":"room-pack-sync-title",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:"MISSING DUB MOD"}),t.jsx("h2",{id:"room-pack-sync-title",children:io(St.packTitle)}),t.jsx("p",{children:St.source==="workshop"?"This room uses a Workshop mod that is not installed in your local library.":"This room uses a local-only mod from the host. Voxalike cannot download private browser files from another player."})]}),t.jsxs("div",{className:"panel-body control-stack",children:[St.workshopPack?t.jsxs("div",{className:"room-pack-sync-summary",children:[St.workshopPack.coverUrl?t.jsx("img",{alt:"",src:St.workshopPack.coverUrl}):t.jsx("div",{className:"workshop-cover workshop-cover-empty",children:t.jsx(bt,{size:30})}),t.jsxs("div",{children:[t.jsx("strong",{children:St.workshopPack.title}),t.jsxs("span",{children:["By ",St.workshopPack.creator||"Unknown"]})]})]}):t.jsx("div",{className:"room-pack-sync-note",children:"Ask the host to upload this mod to Workshop, or send you the ZIP/folder so you can import it in Mods."}),t.jsxs("div",{className:"button-row",children:[t.jsxs("button",{className:"ghost-button",type:"button",disabled:a||Ar,onClick:()=>{zp()},children:[Ar?t.jsx("span",{className:"inline-button-spinner","aria-hidden":"true"}):t.jsx(it,{size:18}),Ar?"Reconnecting...":"Reconnect"]}),St.workshopPack?t.jsxs("button",{className:`action-button ${a?"installing-button":""}`,type:"button",disabled:a,onClick:()=>{UP()},children:[t.jsx(Gt,{size:18}),a?t.jsx(jn,{percent:n}):"Install from Workshop"]}):null,t.jsx("button",{className:"ghost-button",type:"button",onClick:FP,children:"Close"}),t.jsx("button",{className:"danger-button",type:"button",onClick:()=>{Wl()},children:"Leave Room"})]})]})]})})}function hy(){return Mg?t.jsx("div",{className:"mod-import-error-backdrop",role:"presentation",onClick:()=>Hn(void 0),children:t.jsxs("section",{className:"mod-import-error-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"mod-import-error-title","aria-describedby":"mod-import-error-description",onClick:a=>a.stopPropagation(),children:[t.jsxs("div",{className:"mod-import-error-heading",children:[t.jsx(ud,{size:24,"aria-hidden":"true"}),t.jsxs("div",{children:[t.jsx("span",{children:"IMPORT BLOCKED"}),t.jsx("h2",{id:"mod-import-error-title",children:"Convert this pack before importing"})]})]}),t.jsx("p",{id:"mod-import-error-description",children:Mg}),t.jsx("div",{className:"mod-import-error-actions",children:t.jsx("button",{className:"action-button",type:"button",onClick:()=>Hn(void 0),children:"Got it"})})]})}):null}function pm(){if(!vr)return null;const a=vr.source==="mic-check",n=vr.source==="take-processing";return t.jsx("div",{className:"microphone-runtime-backdrop",role:"presentation",children:t.jsxs("section",{className:"microphone-runtime-dialog",role:"dialog","aria-modal":"true","aria-labelledby":"microphone-runtime-title",children:[t.jsxs("div",{className:"microphone-runtime-heading",children:[t.jsx(ud,{size:24,"aria-hidden":"true"}),t.jsxs("div",{children:[t.jsx("span",{children:n?"TAKE PROCESSING FAILED":"MICROPHONE INTERRUPTED"}),t.jsx("h2",{id:"microphone-runtime-title",children:a?"Mic Check stopped":"This take was not saved"})]})]}),t.jsx("p",{children:vr.message}),t.jsx("p",{className:"microphone-runtime-help",children:n?"Retry this line. If it repeats, copy the diagnostic and send it with your bug report.":`Tap Reconnect Mic to request a fresh microphone stream and retry ${a?"Mic Check":"this line"}. If it repeats, close other apps using the microphone, copy the diagnostic and send it with your bug report.`}),t.jsxs("div",{className:"microphone-runtime-actions",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{hc(vr.technical).then(()=>P("Microphone diagnostic copied."),()=>P("Could not copy microphone diagnostic."))},children:[t.jsx(Ga,{size:17}),"Copy Diagnostic"]}),t.jsxs("button",{className:"action-button",type:"button",onClick:nR,children:[t.jsx(it,{size:17}),n?"Retry Line":"Reconnect Mic"]}),t.jsx("button",{className:"icon-button",type:"button",title:"Close","aria-label":"Close microphone diagnostic",onClick:()=>{si.current=void 0,To(void 0)},children:t.jsx(Tt,{size:18})})]})]})})}function gy(a=!1,n=!1){const s=Rn(),d=Va(),u=vn(),h=n?"field-label inline-pack-picker":"field-label",x=n?"joined-room-pack-select":"pre-room-pack-select",g=n?null:t.jsxs("span",{className:"claim-help room-pack-help",children:[t.jsx("button",{"aria-describedby":"room-mod-sync-help","aria-label":r("How multiplayer mods sync"),
159className:"claim-help-button lobby-help-button",type:"button",children:"?"}),t.jsx("span",{className:"claim-help-popover room-pack-help-popover",id:"room-mod-sync-help",role:"tooltip",children:r("Workshop subscriptions install automatically for signed-in players. Local mods must be downloaded and imported by everyone in the room.")})]});return s.length?t.jsxs("div",{className:h,children:[t.jsxs("div",{className:"room-pack-field-heading",children:[t.jsx("label",{className:"room-pack-field-label",htmlFor:x,children:r(n?"Change Mod":"Choose a Mod")}),g]}),t.jsx("div",{className:"room-pack-select-row",children:t.jsx("select",{className:"select-input",id:x,value:u?.id??"",onChange:S=>Gu(S.target.value),disabled:a,children:s.map(S=>t.jsx("option",{value:S.id,children:io(S.summary?.title||S.name)},S.id))})})]}):t.jsxs("div",{className:"room-pack-placeholder",children:[t.jsxs("div",{className:"room-pack-field-heading",children:[t.jsx("span",{className:"room-pack-field-label",children:r("Choose a Mod")}),g]}),t.jsx("strong",{children:d?io(d.title):r("Import a mod first")})]})}function fy(a=!1){const n=[{value:"coop",label:"Co-op",description:"Rotate turns, claim clips, or claim roles."},{value:"pvp",label:"PVP",description:"Everyone records the same clips and compares scores."}];return t.jsx("div",{className:"room-mode-picker","aria-label":r("Multiplayer mode"),children:n.map(s=>{const d=mt===s.value;return t.jsxs("div",{className:`room-mode-option${d?" selected":""}`,children:[t.jsx("button",{"aria-pressed":d,className:`room-mode-select ${d?"action-button selected":"ghost-button"}`,disabled:a,type:"button",onClick:()=>us(s.value),children:t.jsx("span",{children:r(s.label)})}),t.jsxs("span",{className:"claim-help room-mode-help",children:[t.jsx("button",{"aria-describedby":`room-mode-${s.value}-help`,"aria-label":`${s.label} explanation`,className:"claim-help-button lobby-help-button",type:"button",children:"?"}),t.jsx("span",{className:"claim-help-popover room-mode-help-popover",id:`room-mode-${s.value}-help`,role:"tooltip",children:r(s.description)})]})]},s.value)})})}function vC(a=!1){if(!v)return null;const n=Math.max(v.players.length,v.maxPlayers??Td),s=Math.max(rD,v.players.length);return t.jsxs("div",{className:"room-assignment-mode-field room-player-limit-field",children:[t.jsxs("div",{className:"room-player-limit-heading",children:[t.jsx("label",{htmlFor:"room-player-limit",children:r("Player Limit")}),t.jsx("strong",{children:Kc})]}),t.jsxs("div",{className:"room-player-limit-control",children:[t.jsx("input",{"aria-label":r("Player Limit"),className:"room-player-limit-slider",disabled:a||Xu,id:"room-player-limit",max:Td,min:s,step:1,type:"range",value:Math.max(s,Kc),onChange:d=>Kg(Number(d.target.value))}),t.jsx("button",{className:"ghost-button room-player-limit-confirm",disabled:a||Xu||Kc===n,type:"button",onClick:()=>{NR(Kc)},children:r("Confirm")})]})]})}function kC(a=!1){return mt!=="coop"?null:t.jsxs("div",{className:"room-assignment-settings",children:[t.jsxs("label",{className:"field-label room-assignment-mode-field",children:[r("Co-op Assignment"),t.jsxs("select",{className:"select-input",value:ba,onChange:n=>ps(n.target.value),disabled:a,children:[t.jsx("option",{value:"round-robin",children:r("Round Robin")}),t.jsx("option",{value:"claim",children:r("Claim Mode")}),t.jsx("option",{value:"role",children:r("Role Claim")})]})]}),ba==="round-robin"?t.jsxs("div",{className:"room-claim-setting",children:[t.jsxs("div",{className:"claim-setting-title",children:[t.jsx("strong",{children:r("Randomize Player Order")}),t.jsxs("span",{className:"claim-help",children:[t.jsx("button",{"aria-describedby":"random-player-order-help","aria-label":"Random player order explanation",className:"claim-help-button",type:"button",children:"?"}),t.jsx("span",{className:"claim-help-popover",id:"random-player-order-help",role:"tooltip",children:r("Shuffles the player order once when the game starts, then continues rotating fairly.")})]})]}),t.jsx("button",{"aria-label":el?"Use joined player order":"Randomize player order","aria-pressed":el,className:`claim-mode-switch ${el?"on":""}`,disabled:a,type:"button",onClick:()=>tf(n=>!n),title:"Randomize player order",children:t.jsx("span",{"aria-hidden":"true"})})]}):null]})}function wC(a=!1){return mt!=="pvp"?null:t.jsxs("label",{className:"field-label room-assignment-mode-field",children:[r("PVP Format"),t.jsxs("select",{className:"select-input",value:qu,onChange:n=>ef(n.target.value),disabled:a,children:[t.jsx("option",{value:"turns",children:r("Turn Battle")}),t.jsx("option",{value:"parallel",children:r("Parallel Race")})]}),t.jsx("small",{children:r(qu==="parallel"?"Players record separately. Faster players get a higher score.":"Players take turns recording the same clip before moving to the next one.")})]})}function SC(a=!1){return t.jsxs("div",{className:"room-claim-setting",children:[t.jsxs("div",{className:"claim-setting-title",children:[t.jsx("strong",{children:r("Allow Re-records")}),t.jsxs("span",{className:"claim-help",children:[t.jsx("button",{"aria-describedby":"retake-mode-help","aria-label":"Re-record setting explanation",className:"claim-help-button",type:"button",children:"?"}),t.jsx("span",{className:"claim-help-popover",id:"retake-mode-help",role:"tooltip",children:r("On: players can clear their current multiplayer take and record it again before submitting.")})]})]}),t.jsx("button",{"aria-label":tl?"Disable re-records":"Enable re-records","aria-pressed":tl,className:`claim-mode-switch ${tl?"on":""}`,disabled:a,type:"button",onClick:()=>of(n=>!n),title:"Toggle multiplayer re-records",children:t.jsx("span",{"aria-hidden":"true"})})]})}function xC(){return v?v.status==="playing"?t.jsx("section",{className:"panel room-panel",children:t.jsxs("div",{className:"panel-header",children:[t.jsx("span",{children:r("DUB MOD")}),t.jsx("h2",{children:io(v.game?.packTitle||Qf().title)}),t.jsx("p",{children:r("{count} players in room",{count:v.players.length})})]})}):t.jsxs(t.Fragment,{children:[my(v),t.jsx(X0,{isHost:Js,pack:Qf(),packSelector:Js?t.jsxs(t.Fragment,{children:[fy(!!v.recovery),vC(!1),kC(!!v.recovery),wC(!!v.recovery),SC(!!v.recovery),gy(!!v.recovery,!0)]}):void 0,onLeaveRoom:Wl})]}):t.jsx(K0,{connectionPending:or,publicLanguageFilter:Ou,publicRooms:yx,publicRoomsError:wx,publicRoomsLoading:kx,publicStatusFilter:Uu,roomLanguage:zu,signedIn:!!ee,visibility:Yn,packSelector:t.jsxs(t.Fragment,{children:[fy(!1),gy(!1)]}),playerName:is,roomCodeInput:Vu,roomMessage:Px,recentDubs:Kx(),onCreateRoom:()=>{yR()},onJoinRoom:()=>{_f()},onJoinPublicRoom:vR,onPublicLanguageFilterChange:Sx,onPublicStatusFilterChange:xx,onRefreshPublicRooms:()=>{Fl()},onRoomLanguageChange:bx,onSignIn:ft,onVisibilityChange:fx,onPlayerNameChange:a=>{Hg.current=!0,Jg(a)},onRoomCodeChange:fa})}function RC(){return t.jsxs("label",{className:"field-label",children:[r("Input Device"),t.jsxs("select",{className:"select-input",value:b.selectedDeviceId,onChange:a=>{cR(a.target.value)},children:[t.jsx("option",{value:"default",children:r("Default")}),b.devices.map((a,n)=>t.jsx("option",{value:a.deviceId,children:a.label||r("Microphone {number}",{number:n+1})},a.deviceId||n))]})]})}function PC(){return t.jsxs("section",{className:"panel language-settings-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("h3",{children:r("Interface Language")}),t.jsx("p",{children:r("Choose the language used by the game interface.")})]}),t.jsx("div",{className:"panel-body control-stack",children:t.jsxs("label",{className:"field-label",children:[r("Language"),t.jsx("select",{className:"select-input",value:e,onCh
159ange:a=>o(a.target.value),children:ij.map(a=>t.jsx("option",{value:a.value,children:a.label},a.value))})]})})]})}function by(){return t.jsxs(t.Fragment,{children:[t.jsxs("label",{className:"field-label",children:[r("Master Volume"),t.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:$e,onChange:a=>Nx(Number(a.target.value)),className:"volume-slider"}),t.jsxs("span",{className:"volume-value",children:[Math.round($e*100),"%"]})]}),t.jsxs("label",{className:"field-label",children:[r("Sound Effects"),t.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:Dr,onChange:a=>Dx(Number(a.target.value)),className:"volume-slider"}),t.jsxs("span",{className:"volume-value",children:[Math.round(Dr*100),"%"]})]}),t.jsxs("label",{className:"field-label",children:[r("Background Music"),t.jsx("input",{type:"range",min:"0",max:"1",step:"0.01",value:Lr,onChange:a=>Lx(Number(a.target.value)),className:"volume-slider"}),t.jsxs("span",{className:"volume-value",children:[Math.round(Lr*100),"%"]})]}),t.jsxs("label",{className:"field-label",children:[r("Microphone Volume"),t.jsx("input",{type:"range",min:Vw,max:zw,step:Kj,value:mn,disabled:ae||wt||ut,onChange:a=>dR(Number(a.target.value)),"aria-label":r("Microphone Volume"),className:"volume-slider"}),t.jsxs("span",{className:"volume-value",children:[Math.round(mn*100),"%"]}),t.jsx("small",{children:r("Changes recording playback and exports. Scoring is unaffected.")})]})]})}function CC(){return t.jsxs("section",{className:"panel mobile-landscape-audio-panel",children:[t.jsx("div",{className:"panel-header",children:t.jsx("h3",{children:r("Audio Settings")})}),t.jsx("div",{className:"panel-body control-stack",children:by()})]})}function MC(){const a=!Ni(Ee),n=Ee==="raw",s=Eo==="noise"?{className:"noise",icon:t.jsx(XC,{size:18}),title:"Stay Silent",body:"Measuring background noise for 2 seconds."}:Eo==="voice"?{className:"voice",icon:t.jsx(Cy,{size:18}),title:"Speak Normally",body:"Record your voice at normal volume for 3-5 seconds."}:null,d=a?Eo==="voice"?"Speak at your normal volume for 3-5 seconds.":he?n?"Listen to the sample to hear your microphone without voice processing.":"Listen to the sample to hear your browser's built-in noise reduction.":n?"Record a short sample to hear your microphone without voice processing.":"Record a short sample to hear your browser's built-in noise reduction.":Eo==="noise"?"Stay silent for 2 seconds. Measuring background noise.":Eo==="voice"?"Speak at your normal volume for 3-5 seconds.":he?he.applied?"This mic check is applied to future dubbing recordings.":"Compare the samples, then apply this cleanup to future recordings.":kr?"Your saved mic calibration is applied to future dubbing recordings on this browser.":"Stay silent for 2 seconds, then speak normally for 3-5 seconds. The result can be applied to in-game noise cleanup.",u=`${Jt>0?"+":""}${Jt} ms`;return t.jsxs("section",{className:"panel mic-check-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("h3",{children:r("Mic Check")}),t.jsx("p",{children:r(d)})]}),t.jsxs("div",{className:"panel-body control-stack",children:[t.jsxs("div",{className:"mic-check-device-row",children:[RC(),t.jsx("span",{children:!a&&kr&&b.status==="idle"?r("Calibration saved · mic reconnects when needed"):r(tL(b.status,b.errorMessage))})]}),t.jsxs("div",{className:"mic-processing-row",children:[t.jsxs("label",{className:"field-label",children:[r("Voice Processing"),t.jsxs("select",{className:"select-input",value:Ee,disabled:ae||wt||ut,onChange:h=>{uR(h.target.value)},children:[t.jsx("option",{value:"raw",children:r("Raw")}),t.jsx("option",{value:"natural",children:r("Natural")}),t.jsx("option",{value:"clean",children:r("Clean voice")})]})]}),t.jsx("span",{children:r(Ee==="raw"?"No browser or Voxalike voice processing.":Ee==="natural"?"Uses only your browser's built-in noise reduction.":"Stronger cleanup for fans, keyboards and noisy rooms.")})]}),t.jsxs("div",{className:"mic-processing-row recording-sync-row",children:[t.jsxs("label",{className:"field-label",children:[r("Recording Sync"),t.jsx("input",{type:"range",min:Ww,max:$w,step:ph,value:Jt,disabled:ae||wt,onChange:h=>lR(Number(h.target.value)),"aria-label":r("Voice timing"),className:"volume-slider"}),t.jsxs("span",{className:"recording-sync-scale",children:[t.jsx("span",{children:r("Earlier")}),t.jsx("span",{children:r("Later")})]})]}),t.jsxs("span",{className:"recording-sync-summary",children:[t.jsx("strong",{children:u}),r("Left: earlier. Right: later.")]})]}),t.jsx(f0,{label:r("Input Level"),rms:be.rms,peak:be.peak}),Xa?t.jsxs("div",{className:"mic-diagnostic",role:"alert",children:[t.jsxs("div",{children:[t.jsx("strong",{children:r("Microphone could not start")}),t.jsx("span",{children:r(Xa.message)})]}),t.jsx("button",{className:"ghost-button icon-button",type:"button",onClick:()=>{hc(Xa.technical).then(()=>P("Mic diagnostics copied."),()=>P("Could not copy mic diagnostics."))},title:"Copy mic diagnostics","aria-label":"Copy mic diagnostics",children:t.jsx(Ga,{size:16})})]}):null,wt&&s?t.jsxs("div",{className:`mic-check-phase-card ${s.className}`,role:"status",children:[t.jsx("span",{className:"mic-check-phase-icon",children:s.icon}),t.jsxs("div",{className:"mic-check-phase-copy",children:[t.jsx("strong",{children:r(s.title)}),t.jsx("span",{children:r(s.body)})]})]}):!he&&a?t.jsxs("div",{className:"mic-calibration-status applied",children:[t.jsx("strong",{children:r(n?"Raw microphone active":"Browser noise reduction active")}),t.jsx("span",{children:r(n?"Browser and Voxalike voice processing are disabled; no calibration is needed.":"Your browser handles noise reduction automatically; no calibration is needed.")})]}):he?null:t.jsxs("div",{className:`mic-calibration-status ${kr?"applied":"missing"}`,children:[t.jsx("strong",{children:r(kr?"Mic calibration applied":"Mic calibration not applied")}),t.jsx("span",{children:r(kr?"Noise cleanup is active for future recordings.":"Run Mic Check and apply it to improve noise cleanup.")})]}),he?t.jsxs("div",{className:"mic-check-card",children:[t.jsxs("div",{className:"mic-check-card-title",children:[t.jsx("strong",{children:he.summary}),t.jsx("span",{children:r(a?"Ready":he.applied?"Applied":"Ready to apply")})]}),t.jsxs("div",{className:"mic-check-metrics",children:[t.jsxs("span",{children:[r("Noise:")," ",r(he.noiseLabel)]}),t.jsxs("span",{children:[r("Volume:")," ",r(he.volumeLabel)]}),t.jsxs("span",{children:[r("Peaks:")," ",r(he.peakLabel)]})]})]}):null,wt?t.jsx("div",{className:"button-row",children:t.jsxs("button",{className:"ghost-button",type:"button",onClick:Lp,title:"Cancel mic check",children:[t.jsx(pd,{size:18}),r("Cancel")]})}):he&&a?t.jsx("div",{className:"mic-check-actions",children:t.jsxs("div",{className:"button-row",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>Vp("raw"),title:n?"Play unprocessed microphone sample":"Play browser-processed sample",children:[t.jsx(_e,{size:18}),r("Play sample")]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{Ds()},title:"Run mic check again",children:[t.jsx(it,{size:18}),r("Retake")]})]})}):he?t.jsxs("div",{className:"mic-check-actions",children:[t.jsxs("div",{className:"button-row",children:[t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>Vp("raw"),title:"Play raw sample",children:[t.jsx(_e,{size:18}),r("Raw")]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>Vp("cleaned"),title:"Play cleaned sample",children:[t.jsx(_e,{size:18}),r("Cleaned")]}),t.jsxs("button",{className:"ghost-button",type:"button",onClick:()=>{Ds()},title:"Run mic check again",children:[t.jsx(it,{size:18}),r("Retake")]})]}),t.jsxs("button",{className:"action-button",type:"button",disabled:he.applied,onClick:sR,title:"Ap
159ply mic check to future recordings",children:[t.jsx(nt,{size:18}),r(he.applied?"Applied":"Apply Mic Check")]})]}):kr?t.jsxs("div",{className:"mic-check-actions",children:[t.jsx("div",{className:"button-row",children:t.jsxs("button",{className:"ghost-button",type:"button",disabled:ut,onClick:()=>{Ds()},title:"Run mic check again",children:[t.jsx(it,{size:18}),r("Retake")]})}),t.jsxs("button",{className:"action-button",type:"button",disabled:!0,title:"Mic calibration is applied",children:[t.jsx(nt,{size:18}),r("Applied")]})]}):t.jsx("div",{className:"button-row",children:t.jsxs("button",{className:"action-button",type:"button",disabled:ut,onClick:()=>{Ds()},title:"Start mic check",children:[t.jsx(Cy,{size:18}),r(ut?"Connecting Mic...":"Start Mic Check")]})})]})]})}function jC(){return t.jsxs("section",{className:"panel audio-settings-panel",children:[t.jsxs("div",{className:"panel-header",children:[t.jsx("h3",{children:r("Audio Settings")}),t.jsx("p",{children:r("Control game audio levels.")})]}),t.jsx("div",{className:"panel-body control-stack",children:by()})]})}function Ei(a){const n=an.current;n&&n!==a&&Ba(n),an.current=a,ux(a),tr(Wy(a?.warnings))}function yy(a){const n=new Set(a);Yc.current.forEach(s=>{!n.has(s)&&s!==an.current&&Ba(s)}),Yc.current=a,px(a)}async function _s(){const a=await Zv();yy(a)}function TC(){Nr(!1),Lu("setup"),ns("menu")}function mm(a){hx(a),Nr(!1),Ir(!1),Lu("judging")}async function vy(a){if(a){ha(!0),Kt("Checking judge pack..."),tr([]);try{const n=await Kv(a);Ei(n),await _s();const s=n.warnings?.length?` ${n.warnings.length} judge slot${n.warnings.length===1?"":"s"} fell back to default.`:"";Kt(`${n.title} imported.${s}`)}catch(n){Kt(n instanceof Error?n.message:"Judge pack import failed.")}finally{ha(!1)}}}async function hm(){ha(!0),Kt(""),tr([]);try{await fA(),Ei(void 0),await _s(),Kt("Default Voxalike judges enabled.")}catch(a){Kt(a instanceof Error?a.message:"Judge pack could not be deleted.")}finally{ha(!1)}}async function EC(a){if(a){ua("import"),fo("Checking theme package...");try{const n=await L(a);await D(n.id),fo(n.warnings?.length?`${n.title} enabled with ${n.warnings.length} asset fallback${n.warnings.length===1?"":"s"}.`:`${n.title} imported and enabled.`)}catch(n){fo(n instanceof Error?n.message:"Theme import failed.")}finally{ua(void 0)}}}async function AC(a){ua(a),fo("");try{await D(a),fo("Theme enabled.")}catch(n){fo(n instanceof Error?n.message:"Theme could not be enabled.")}finally{ua(void 0)}}async function IC(){ua("default"),fo("");try{await W(),fo("Default Voxalike theme enabled.")}catch(a){fo(a instanceof Error?a.message:"Default theme could not be restored.")}finally{ua(void 0)}}async function NC(a){ua(a),fo("");try{await j(a),fo("Theme deleted.")}catch(n){fo(n instanceof Error?n.message:"Theme could not be deleted.")}finally{ua(void 0)}}}function Co({active:e,authLoading:o,onMenu:r,onNavigate:i,onSignIn:c,onSignOut:l,showAuth:p=!0,showNav:f=!0,user:b}){const{getThemeAsset:y}=Gh(),{t:w}=Be(),M=[{label:"PLAY",view:"menu",href:"/"},{label:"NEWS",view:"news",href:"/news"},{label:"GUIDE",view:"guide",href:"/guide"},{label:"WORKSHOP",view:"workshop",href:"/workshop"}],E=!!b?.supporterIdentities.includes("supporter"),z=b?b.displayName||b.email:w("Sign in"),[N,j]=m.useState(!1);function D(B){B.currentTarget.contains(B.relatedTarget)||j(!1)}function I(){j(!1),l?.()}return t.jsxs("nav",{className:"game-topbar",children:[t.jsxs("button",{className:"game-logo",type:"button",onClick:r,title:Lm.name,"aria-label":Lm.name,children:[t.jsx("img",{alt:Lm.name,src:y("logo")}),t.jsx("span",{className:"alpha-badge","aria-hidden":"true",children:"ALPHA"})]}),f?t.jsxs("div",{className:"game-nav",children:[M.map(B=>t.jsx("a",{className:B.label===e?"active":"",href:B.href,onClick:L=>{i&&(L.preventDefault(),i(B.view))},children:w(B.label)},B.label)),t.jsxs("a",{className:`supporter-nav-link${E?" supporter-active":""}${e==="SUPPORTER"?" current":""}`,href:"/support",title:w(E?"Supporter active":"Become a Supporter"),"aria-label":w(E?"Supporter active":"Become a Supporter"),"aria-current":e==="SUPPORTER"?"page":void 0,onClick:B=>
159{i&&(B.preventDefault(),i("support"))},children:[t.jsx("span",{"aria-hidden":"true",children:"â¦"}),w("SUPPORTER")]})]}):t.jsx("span",{className:"game-topbar-spacer","aria-hidden":"true"}),p?t.jsxs("div",{className:"account-menu",onBlur:D,children:[t.jsxs("button",{className:`sign-button${b?" account-trigger":""}`,type:"button",disabled:o&&!b,onClick:b?()=>j(B=>!B):c,title:w(b?"Account menu":"Sign in"),"aria-expanded":b?N:void 0,"aria-haspopup":b?"menu":void 0,children:[b?.avatarUrl?t.jsx("span",{className:`account-avatar-wrap${b.supporterAvatarFrame?` ${eu(b.supporterAvatarFrame)}`:""}`,children:t.jsx("img",{alt:"",className:"account-avatar",src:b.avatarUrl})}):t.jsx(pw,{size:17}),b?t.jsx(_o,{name:z,identity:b.activeSupporterIdentity??void 0,nameColor:b.supporterNameColor}):t.jsx("span",{children:z}),b?t.jsx(_C,{className:"account-chevron",size:13,"aria-hidden":"true"}):null,b&&b.unreadNotificationCount>0?t.jsx("strong",{className:"account-notification-badge","aria-label":`${b.unreadNotificationCount} unread notifications`,children:Math.min(b.unreadNotificationCount,99)}):null]}),b&&N?t.jsxs("div",{className:"account-menu-panel",role:"menu",children:[t.jsxs("a",{href:"/notifications",role:"menuitem",onClick:()=>j(!1),children:[t.jsx(rh,{size:16}),t.jsx("span",{children:w("Notifications")}),b.unreadNotificationCount>0?t.jsx("strong",{className:"account-menu-count",children:Math.min(b.unreadNotificationCount,99)}):null]}),t.jsxs("a",{href:`/workshop/creators/${encodeURIComponent(b.id)}`,role:"menuitem",onClick:()=>j(!1),children:[t.jsx(YC,{size:16}),t.jsx("span",{children:w("Profile")})]}),t.jsxs("a",{href:"/profile?account=1",role:"menuitem",onClick:()=>j(!1),children:[t.jsx(KC,{size:16}),t.jsx("span",{children:w("Account details")})]}),b.isAdmin?t.jsxs("a",{href:"/workshop/admin/studio",role:"menuitem",onClick:()=>j(!1),children:[t.jsx(ZC,{size:16}),t.jsx("span",{children:w("Studio")})]}):null,b.supporterIdentities.includes("community_admin")||b.supporterIdentities.includes("community_contributor")||sh()?t.jsxs("a",{href:b.supporterIdentities.includes("community_admin")?"/profile":b.supporterIdentities.includes("community_contributor")?"/contributor/status":"/contributor",role:"menuitem",onClick:()=>j(!1),children:[b.supporterIdentities.includes("community_admin")?t.jsx(nw,{size:16}):b.supporterIdentities.includes("community_contributor")?t.jsx(nt,{size:16}):t.jsx(In,{size:16}),t.jsx("span",{className:"account-benefit-label account-benefit-label-full",children:b.supporterIdentities.includes("community_admin")?w("Admin Benefits Active"):b.supporterIdentities.includes("community_contributor")?w("Contributor Benefits Active"):w("Apply for Contributor")}),t.jsx("span",{className:"account-benefit-label account-benefit-label-mobile",children:b.supporterIdentities.includes("community_admin")?w("Admin Active"):b.supporterIdentities.includes("community_contributor")?w("Contributor Active"):w("Contributor Program")})]}):null,t.jsxs("a",{className:"account-creator-link",href:"/workshop/upload",role:"menuitem",onClick:()=>j(!1),children:[t.jsx(ah,{size:16}),t.jsx("span",{children:w("Creator Workshop")})]}),t.jsxs("a",{href:"/support?redeem=open",role:"menuitem",onClick:()=>j(!1),children:[t.jsx(Vd,{size:16}),t.jsx("span",{children:w("Redeem code")})]}),t.jsxs("button",{type:"button",role:"menuitem",onClick:I,children:[t.jsx(wc,{size:16}),t.jsx("span",{children:w("Sign out")})]})]}):null]}):t.jsx("span",{className:"game-topbar-spacer","aria-hidden":"true"})]})}function jn({percent:e,pendingLabel:o="Installing"}){const r=typeof e=="number"?Math.max(0,Math.min(100,Math.round(e))):void 0,i={"--install-progress":`${r??0}%`};return t.jsxs("span",{className:`install-progress-label ${r===void 0?"indeterminate":""}`,style:i,children:[t.jsx("span",{className:"install-progress-ring","aria-hidden":"true"}),t.jsx("span",{children:r===void 0?o:`${r}%`})]})}function Nk(){const{t:e}=Be(),o="voxalike-mobile-landscape-prompt-dismissed",[r,i]=m.useState(()=>{try{return sessionStorage.getItem(o)==="1"}catch{return!1}});return r?null:t.jsxs("aside",{className:"mobile-landscape-prompt",role:"dialog","aria-modal":"true","aria-labelledby":"mobile-landscape-title",children:[t.jsxs("div",{className:"mobile-landscape-icon","aria-hidden":"true",children:[t.jsx(uw,{size:34}),t.jsx(it,{size:22})]}),t.jsx("strong",{id:"mobile-landscape-title",children:e("Rotate your phone")}),t.jsx("p",{children:e("Landscape gives the video and recording controls more room.")}),t.jsx("button",{className:"ghost-button",type:"button",onClick:()=>{try{sessionStorage.setItem(o,"1")}catch{}i(!0)},children:e("Stay in portrait")})]})}function qo(){const{t:e}=Be();return t.jsxs("footer",{className:"game-footer",children:[t.jsx("div",{className:"footer-brandline",children:"© 2026 Voxalike"}),t.jsxs("nav",{className:"footer-platform-links","aria-label":"Community links",children:[t.jsx("span",{className:"footer-platform-label",children:e("Community")}
159),t.jsx("a",{className:"footer-platform-link itch",href:EN,target:"_blank",rel:"noopener noreferrer","aria-label":"Voxalike on itch.io",title:"itch.io",children:t.jsx("img",{src:"/itchio-icon.svg",alt:"Voxalike on itch.io"})}),t.jsx("a",{className:"footer-platform-link discord",href:AN,target:"_blank",rel:"noopener noreferrer","aria-label":"Join Voxalike on Discord",title:"Discord",children:t.jsx("img",{src:"/discord-icon.svg",alt:"Join Voxalike on Discord"})}),t.jsx("a",{className:"footer-platform-link reddit",href:IN,target:"_blank",rel:"noopener noreferrer","aria-label":"Voxalike on Reddit",title:"Reddit",children:t.jsx("img",{src:"/reddit-icon.svg",alt:"Voxalike on Reddit"})})]}),t.jsx("nav",{className:"footer-legal-links","aria-label":"Footer links",children:TN.map(o=>t.jsx("a",{href:o.href,children:o.label},o.href))})]})}async function RD(e){const o=await Lj(e.audioFile,e.audioUrl);return Iw(o.bytes,o.mimeType)}async function Dk(e){return Iw(await e.arrayBuffer(),e.type)}function PD(e){const o=Math.max(0,Date.now()-e),r=Math.floor(o/6e4);if(r<1)return"just now";if(r<60)return`${r}m ago`;const i=Math.floor(r/60);return i<24?`${i}h ago`:`${Math.floor(i/24)}d ago`}async function $a(e,o){const r=await Dw(o);return hu(e,r.bytes,r.mimeType)}function Ed(e,o){const r=e.createBuffer(1,o.samples.length,o.sampleRate),i=new Float32Array(o.samples.length);return i.set(o.samples),r.copyToChannel(i,0),r}function Lk(e){return{samples:new Float32Array(e.samples),sampleRate:e.sampleRate}}function CD(e,o){const r=Math.max(0,Math.min(e.samples.length,Math.round(o*e.sampleRate)));return{samples:e.samples.slice(r),sampleRate:e.sampleRate}}function pu(){const e=new Error("Video export cancelled.");return e.name="AbortError",e}function qa(e){return e instanceof Error&&e.name==="AbortError"}function MD(e){return e instanceof Ah?!0:!(e instanceof Error)||qa(e)?!1:["timed out","did not start for video export","did not finish","playback stalled","Video recorder failed"].some(o=>e.message.includes(o))}function Km(e,o,r,i,c,l){const p=c instanceof Error?c.name:"UnknownError",f=c instanceof Error?c.message:"Unknown video export error",b=f.toLowerCase(),y=yt();let w="Video export failed. Your recordings are still saved on this device.";b.includes("not allowed by the user agent")||b.includes("notallowed")?w=y?"Safari blocked video playback. Tap Compatibility Retry to try again.":"The browser blocked media playback. Keep this page open and retry.":p==="ExportSourceCodecError"?w="This mod's video is not compatible with iPhone Safari. You can save your audio or use a desktop browser.":b.includes("decoding failed")||b.includes("format is not supported")?w="Safari cannot decode this mod's video. Your recordings are safe; try a desktop browser or save the audio track.":b.includes("network error")?w="The mod video could not load. Check your connection and try again.":b.includes("audio processing did not start")?w=y?"iPhone audio could not start. Tap Compatibility Retry.":"Browser audio could not start. Tap Compatibility Retry.":b.includes("failed to load or decode")||b.includes("no supported source")?w="This mod's source video could not be read on this device.":b.includes("frames stopped advancing")||b.includes("playback stalled")?w="Video rendering stopped. Tap Compatibility Retry.":b.includes("failed to fetch")||b.includes("networkerror")?w="The source video could not be loaded. Check your connection and retry.":(b.includes("recorder")||b.includes("did not finish"))&&(w="The browser could not finish the video. Your recordings are still saved.");const M=["Voxalike video export diagnostic",`Time: ${new Date().toISOString()}`,`Pack: ${e.packTitle}`,`Pack ID: ${e.packId}`,`Workshop slug: ${e.workshopPackSlug??"local"}`,`Format: ${o}`,`Stage: ${r}`,`Attempt: ${i}`,`Profile: ${l?.renderPath??"not-started"}`,`Resolution: ${l?`${l.exportWidth}x${l.exportHeight}`:"unknown"}`,`Frame rate: ${l?.frameRate??"unknown"}`,`Error: ${p}: ${f}`,`Media error code: ${c instanceof Bi?c.mediaErrorCode??"unknown":"unknown"}`,`Platform: ${navigator.platform||"unknown"}`,`Browser: ${navigator.userAgent}`,`CPU threads: ${navigator.hardwareConcurrency||"unknown"}`,`Device memory: ${navigator.deviceMemory??"unknown"} GB`].join(` 160`);return{workId:e.id,format:o,summary:w,details:M}}function zi(e){if(e?.aborted)throw pu()}async function Vk(e,o){return o?(zi(o),Promise.race([e,new Promise((r,i)=>{const c=()=>i(pu()),l=()=>o.removeEventListener("abort",c);o.addEventListener("abort",c,{once:!0}),e.then(l,l)})])):e}function Ha(e){return e.readyState>=HTMLMediaElement.HAVE_METADATA?Promise.resolve():new Promise(o=>{const r=()=>{e.removeEventListener("loadedmetadata",r),e.removeEventListener("error",r),o()};e.addEventListener("loadedmetadata",r,{once:!0}),e.addEventListener("error",r,{once:!0}),e.load()})}function zk(e){return e.readyState>=HTMLMediaElement.HAVE_METADATA?Promise.resolve():new Promise(o=>{const r=()=>{e.removeEventListener("loadedmetadata",r),e.removeEventListener("error",r),o()};e.addEventListener("loadedmetadata",r,{once:!0}),e.addEventListener("error",r,{once:!0}),e.load()})}function mc(e){return e.seeking?new Promise(o=>{const r=()=>{e.removeEventListener("seeked",r),e.removeEventListener("error",r),o()};e.addEventListener("seeked",r,{once:!0}),e.addEventListener("error",r,{once:!0})}):Promise.resolve()}function jD(e,o){return e.readyState>=HTMLMediaElement.HAVE_FUTURE_DATA?Promise.resolve():new Promise(r=>{const i=window.setTimeout(c,o);function c(){window.clearTimeout(i),e.removeEventListener("canplay",c),e.removeEventListener("error",c),r()}e.addEventListener("canplay",c,{once:!0}),e.addEventListener("error",c,{once:!0})})}function Ok(e){const o=e.currentTime;return!e.paused&&!e.ended&&e.readyState>=HTMLMediaElement.HAVE_CURRENT_DATA?new Promise((r,i)=>{const c=window.setTimeout(()=>{if(f(),e.currentTime>o+.02){r();return}i(new Error("Video did not start moving. This browser may not support this OGV video."))},1200),l=()=>{e.currentTime<=o+.02||(f(),r())},p=()=>{f(),i(new Error("Video playback failed."))},f=()=>{window.clearTimeout(c),e.removeEventListener("timeupdate",l),e.removeEventListener("playing",l),e.removeEventListener("error",p)};e.addEventListener("timeupdate",l),e.addEventListener("playing",l),e.addEventListener("error",p,{once:!0})}):Promise.reject(new Error("Video did not start moving. This browser may not support this OGV video."))}function TD(e){return new Promise(o=>window.setTimeout(o,gN(e)))}function Uk(e){return e.style.position="fixed",e.style.left="0",e.style.top="0",e.
160style.width="2px",e.style.height="2px",e.style.opacity="0.001",e.style.pointerEvents="none",e.setAttribute("aria-hidden","true"),document.body.append(e),()=>{e.remove()}}function ED(e){const o=e,r=o.captureStream??o.mozCaptureStream;if(r)try{return r.call(e)}catch{return}}function AD(e){try{return e.getVideoPlaybackQuality?.().totalVideoFrames??Number.NaN}catch{return Number.NaN}}function ID(e,o){if(typeof e.requestVideoFrameCallback!="function")return()=>{};let r=!1,i=0;const c=()=>{i=e.requestVideoFrameCallback((l,p)=>{r||(o(p.mediaTime,l),c())})};return c(),()=>{r=!0,i&&typeof e.cancelVideoFrameCallback=="function"&&e.cancelVideoFrameCallback(i)}}function Fk(e,o,r,i){if(!Number.isFinite(e.duration)||e.duration<=0||e.ended)return;const c=e.currentTime;if(c>i.lastVideoTime+.02){i.update(c,r);return}const l=Math.min(Math.max(0,o),Math.max(0,e.duration-.04));if(!(l-c<tD||r-i.lastVideoAdvancedAt<oD))try{e.currentTime=l,i.update(l,r),e.play().catch(()=>{})}catch{}}function Bk(e){if(e.state==="recording")try{e.pause()}catch{}}function ND(e){if(e.state==="paused")try{e.resume()}catch{}}function DD(e,o,r){const i=Math.max(0,e.videoStartSeconds??0),c=Number.isFinite(r)&&r>i?Math.max(.65,r-i):Number.POSITIVE_INFINITY,l=e.videoEndSeconds!==void 0&&e.videoEndSeconds>i?e.videoEndSeconds-i:void 0,f=Math.max(.65,o,e.duration??0)+.28,b=l!==void 0?Math.max(f,Math.min(l,f+.75)):f;return Math.max(.65,Math.min(c,b))}function Wk(e){return JI(e).find(o=>MediaRecorder.isTypeSupported(o))??""}function $k(e,o){let r;for(const i of HI(o,RN))try{return new MediaRecorder(e,i)}catch(c){r=c}throw r instanceof Error?r:new Error("Video recorder is unavailable.")}function LD(e,o){e.sort((r,i)=>r.start-i.start);for(let r=0;r<e.length;r+=1){const i=e[r],c=e[r+1];c&&i.end>c.start-.04&&(i.end=Math.max(i.start+.35,c.start-.04)),o!==void 0&&(i.end=Math.min(i.end,o)),i.end<=i.start&&(i.end=i.start+.9)}}function Ad(e,o,r,i,c,l={format:"dub",subtitles:!1}){if(l.format==="shorts"){VD(e,o,r,i,c,l.subtitles!==!1);return}if(e.drawImage(o,0,0,r,i),l.subtitles===!1)return;const p=qS(c,o.currentTime);p&&JS(e,p.text,r,i)}function VD(e,o,r,i,c,l=!0){e.save(),e.fillStyle="#050913",e.fillRect(0,0,r,i),e.save(),e.filter="blur(24px) brightness(0.44) saturate(1.18)",zD(e,o,0,0,r,i),e.restore();const p=HS(o.videoWidth||16,o.videoHeight||9,r,i);p.x=(r-p.width)/2,p.y=(i-p.height)/2,OD(e,o,p.x,p.y,p.width,p.height);const f=l?qS(c,o.currentTime):void 0;f&&JS(e,f.text,r,i,{maxWidthRatio:.86,maxLines:2,fontScale:.038,minFontSize:26,maxFontSize:46,bottomOffsetRatio:.07,boxAlpha:.78}),e.restore()}function qS(e,o){return e.find(r=>r.text.trim().length>0&&o>=r.start&&o<=r.end)}function zD(e,o,r,i,c,l){const p=o.videoWidth||c,f=o.videoHeight||l,b=p/f,y=c/l;let w=0,M=0,E=p,z=f;b>y?(E=f*y,w=(p-E)/2):(z=p/y,M=(f-z)/2),e.drawImage(o,w,M,E,z,r,i,c,l)}function OD(e,o,r,i,c,l){const p=HS(o.videoWidth||16,o.videoHeight||9,c,l);e.drawImage(o,r+p.x,i+p.y,p.width,p.height)}function HS(e,o,r,i){const c=Math.min(r/Math.max(1,e),i/Math.max(1,o)),l=e*c,p=o*c;return{x:(r-l)/2,y:(i-p)/2,width:l,height:p}}function JS(e,o,r,i,c={}){const l=o.trim();if(!l)return;e.save();const p=r*(c.maxWidthRatio??.82),f=c.maxLines??3,b=c.minFontSize??18;let y=Math.round(WD(i*(c.fontScale??.045),c.minFontSize??22,c.maxFontSize??52)),w=[];for(;y>=b&&(e.font=`700 ${y}px Arial, Helvetica, sans-serif`,w=UD(e,l,p),!(w.length<=f));)y-=2;if(w.length>f){const L=Math.max(1,f-1);w=[...w.slice(0,L),FD(e,w.slice(L).join(" "),p)]}const M=y*1.28,E=y*.72,z=y*.42,j=Math.min(p,Math.max(...w.map(L=>e.measureText(L).width)))+E*2,D=M*w.length+z*2,I=(r-j)/2,B=i-D-i*(c.bottomOffsetRatio??.07);e.fillStyle=`rgba(0, 0, 0, ${c.boxAlpha??.72})`,BD(e,I,B,j,D,y*.28),e.fillStyle="#f8fbff",e.textAlign="left",e.textBaseline="middle",e.shadowColor="rgba(0, 0, 0, 0.85)",e.shadowBlur=Math.max(3,y*.14),e.shadowOffsetY=Math.max(1,y*.06),w.forEach((L,_)=>{e.fillText(L,I+E,B+z+M*_+M/2)}),e.restore()}function UD(e,o,r){const i=o.replace(/\s+/g," ").trim(),c=i.split(" "),l=[];let p="";for(const f of c){const b=p?`${p} ${f}`:f;if(e.measureText(b).width<=r||!p){p=b;continue}l.push(p),p=f}return p&&l.push(p),l.length>0?l:[i]}function FD(e,o,r){if(e.measureText(o).width<=r)return o;let c=o.trim();for(;c.length>0&&e.measureText(`${c}...`).width>r;)c=c.slice(0,-1).trimEnd();return`${c}...`}function BD(e,o,r,i,c,l){const p=Math.min(l,i/2,c/2);e.beginPath(),e.moveTo(o+p,r),e.lineTo(o+i-p,r),e.quadraticCurveTo(o+i,r,o+i,r+p),e.lineTo(o+i,r+c-p),e.quadraticCurveTo(o+i,r+c,o+i-p,r+c),e.lineTo(o+p,r+c),e.quadraticCurveTo(o,r+c,o,r+c-p),e.lineTo(o,r+p),e.quadraticCurveTo(o,r,o+p,r),e.closePath(),e.fill()}function WD(e,o,r){return Math.max(o,Math.min(r,e))}async function Gk(e,o,r){const i=Number(e.headers.get("content-length")),c=Number.isFinite(i)&&i>0?i:o&&o>0?o:0;if(r(c>0?0:void 0),!e.body){const b=await e.blob();return r(100),b}const l=e.body.getReader(),p=[];let f=0;for(;;){const{done:b,value:y}=await l.read();if(b)break;
160if(y){const w=new Uint8Array(y.byteLength);w.set(y),p.push(w.buffer),f+=y.byteLength,c>0&&r(Math.min(99,f/c*100))}}return r(100),new Blob(p,{type:e.headers.get("content-type")||"application/zip"})}async function hc(e){if(navigator.clipboard?.writeText){await navigator.clipboard.writeText(e);return}const o=document.createElement("textarea");o.value=e,o.setAttribute("readonly","true"),o.style.position="fixed",o.style.left="-9999px",document.body.append(o),o.select();const r=document.execCommand("copy");if(o.remove(),!r)throw new Error("Copy failed.")}function $D(e,o,r,i=!1){const c=`${r==="shorts"?"shorts":"dubbed"}${i?"":"-no-subtitles"}`;return`${_S(o.packTitle||"voxalike-dub")}-${c}.${GD(e)}`}function GD(e){return e.type.toLowerCase().includes("mp4")?"mp4":"webm"}function _S(e){return e.trim().toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"")||"voxalike"}function io(e){const o=e.trim().replace(/\.[a-z0-9]+$/i,"").replace(/[-_\s]+web$/i,"").replace(/[-_\s]+d[0-9a-z]{4,}$/i,"").replace(/[_-]+/g," ").replace(/\s+/g," ").trim();return o?o.split(" ").map(r=>{const i=r.toLowerCase();return["vs","ii","iii","iv"].includes(i)?i.toUpperCase():`${i.charAt(0).toUpperCase()}${i.slice(1)}`}).join(" "):"Untitled Dub Mod"}function qD(e){return e?.clips.find(o=>o.imageUrl)?.imageUrl}function qk(e){const o=new Map;for(const r of e){r.speakers.forEach(i=>o.set(i.toLowerCase(),i));for(const[i,c]of Object.entries(r.metadata))!i.toLowerCase().includes("character")||!c||c.replace(/[[\]"']/g,"").split(/[,/|]/).map(l=>l.trim()).filter(Boolean).forEach(l=>o.set(l.toLowerCase(),l))}return[...o.values()]}function HD(e){return e.map((o,r)=>({id:o.id,number:r+1,title:o.title.trim()||`Line ${r+1}`,caption:o.caption.trim(),roles:Fi(o.speakers)}))}function JD(e){const o={};for(const r of e)for(const i of Fi(r.roles)){const c=i.toLocaleLowerCase();o[c]=(o[c]??0)+1}return o}function Hk(e){const o=new Map;return e.forEach(r=>{const i=r.trim();i&&o.set(i.toLowerCase(),i)}),[...o.values()].join(" + ")}function _D(e){return e.length>180?"subtitle-copy-dense":e.length>90?"subtitle-copy-long":"subtitle-copy-normal"}function Jk(e){const o=e?.trim();if(!(!o||o.toLowerCase()==="unknown"))return o}function YD(e,o){const r=o.summary?.workshopPackageHash;if(e.packageHash&&r)return e.packageHash!==r;const i=o.summary?.workshopUpdatedAt;return!i||i!==e.updatedAt}function Zm(e,o){return o.workshopId===e.id||!!(o.workshopSlug&&Ye(o.workshopSlug)===Ye(e.slug))}function KD(e,o){return!!e.packageHash&&e.packageHash!==o.packageHash}function _k(e,o){return(e.id===o.packId||Ye(e.title)===Ye(o.packTitle))&&e.clips.length===o.clipCount}function Di(e){if(!(!e?.workshopPackId&&!e?.workshopPackSlug))return{id:e.workshopPackId,slug:e.workshopPackSlug}}function Li(e){return e.id?`id:${e.id}`:`slug:${Ye(e.slug)}`}function Yk(e,o){return o.id&&e.id===o.id?!0:!!(o.slug&&Ye(e.slug)===Ye(o.slug))}function ZD(e,o=!1){return o&&!e?"Pack rating: Loading...":!e||!e.ratingCount||e.ratingAverage===null?"Pack rating: No ratings yet":`Pack rating: ${e.ratingAverage}/5 · ${e.ratingCount} ${e.ratingCount===1?"rating":"ratings"}`}function Xm(e){const o=new Set,r=[];for(const i of e){const c=XD(i);o.has(c)||(o.add(c),r.push(i))}return r}function XD(e){return e.summary?.workshopId?`workshop-id:${e.summary.workshopId}`:e.summary?.workshopSlug?`workshop-slug:${Ye(e.summary.workshopSlug)}`:e.summary?.title&&e.summary.clipCount?["pack",Ye(e.summary.title),Ye(e.summary.creator),e.summary.clipCount,Math.round((e.summary.videoDurationSeconds??0)*10)].join(":"):`source-id:${e.id}`}function Ye(e){return(e??"").trim().toLowerCase().replace(/\.[a-z0-9]+$/i,"").replace(/[-_\s]+web$/i,"").replace(/[^a-z0-9]+/g,"")}function Kk(e){return e.normalize("NFKC").trim().toLocaleLowerCase().replace(/\.(zip|dubmod)$/i,"").replace(/[-_\s]+web$/i,"").replace(/[\p{P}\p{S}\s]+/gu,"")}function Gd(e){return new Promise(o=>window.setTimeout(o,e))}function Zk(e){return new Promise(o=>{const r=new Image;r.decoding="async",r.onload=()=>{if(r.decode){r.decode().then(()=>o()).catch(()=>o());return}o()},r.onerror=()=>o(),r.src=e,r.complete&&o()})}async function QD(e){const o=document.createElement("video");
160o.preload="metadata",o.muted=!0,o.playsInline=!0,o.src=e;try{await Promise.race([Ha(o),Gd(1800)])}finally{o.removeAttribute("src"),o.load()}}function Xk(e,o,r,i,c){const l=o instanceof DOMException||o instanceof Error?o.name:"UnknownError",p=o instanceof Error?o.message:typeof o=="string"?o:"No browser error was returned.",f=`${l} ${p}`.toLowerCase(),b=/network|fetch|timeout|offline|internet|failed to load/.test(f),y=/decode|encoding|codec|audio data|media/.test(f);let w="The game could not prepare this mod. Try again, then copy the error details if it still fails.";return e==="mod-load"?w="The saved mod could not be read. Reinstall it from Workshop and try again.":b||e==="session"?w="Voxalike could not reach the game service. Check the connection and try again.":e==="clip-audio"&&y?w="The first clip audio could not be decoded by this browser. Try Chrome, or report this mod.":e==="clip-audio"?w="The first clip could not be prepared. Reinstall this mod and try again.":e==="recovery-save"&&(w="The browser could not create a recovery save. Check available site storage and try again."),{message:w,technical:["Voxalike single-player start diagnostics",`Stage: ${e}`,`Error: ${l}: ${p}`,`Mod: ${i?.title??r?.summary?.title??r?.name??"unknown"}`,`Mod source: ${r?.kind??i?.source??"unknown"}`,`Clip: ${c?.title??"unknown"}`,`Audio type: ${c?.audioFile.type||"unknown"}`,`Video type: ${c?.videoFile?.type||"none"}`,`Online: ${navigator.onLine?"yes":"no"}`,`Secure context: ${window.isSecureContext?"yes":"no"}`,`Browser: ${navigator.userAgent}`].join(` 161`)}}async function Qk(e,o,r,i){let c="unavailable";try{navigator.permissions&&(c=(await navigator.permissions.query({name:"microphone"})).state)}catch{c="unsupported"}const l=r?.getAudioTracks()[0],p=o instanceof DOMException||o instanceof Error?o.name:"UnknownError",f=o instanceof Error?o.message:typeof o=="string"?o:"No browser error was returned.",b={permission:"The browser did not return a usable microphone stream.","audio-context":"The browser blocked or suspended its audio engine.","audio-worklet":"The preferred recorder could not start.","media-recorder":"The compatible mobile recorder could not start.",recording:"The microphone stopped after recording started."},y={"track-ended":"Your phone disconnected the microphone while this line was recording.","track-muted":"Your phone muted the microphone while this line was recording.","audio-data-stalled":"Microphone sound stopped reaching Voxalike while this line was recording.","media-recorder-error":"The mobile recorder stopped unexpectedly while recording this line.","digital-silence":"The browser stopped returning microphone sound while this line was recording."};return{message:!window.isSecureContext||!navigator.mediaDevices?"A secure connection is required. Disable any VPN or web filter that intercepts HTTPS, then reopen Voxalike.":(i?y[i.code]:void 0)??b[e]??"The mobile recording path failed unexpectedly.",technical:["Voxalike mobile microphone diagnostics",`Stage: ${e}`,`Error: ${p}: ${f}`,`Permission: ${c}`,`Page protocol: ${window.location.protocol}`,`Page host: ${window.location.host}`,`Secure context: ${window.isSecureContext?"yes":"no"}`,`MediaDevices: ${navigator.mediaDevices?"available":"unavailable"}`,`Page visibility: ${document.visibilityState}`,`Standalone: ${Ih()?"yes":"no"}`,`Stream active: ${r?.active??!1}`,`Track state: ${l?.readyState??"missing"}`,`Track enabled: ${l?.enabled??!1}`,`Track muted: ${l?.muted??!1}`,...i?[`Runtime failure: ${i.code}`,`Recorder mode: ${i.recorderMode}`,`Recording elapsed: ${i.elapsedMs} ms`,`Last audio data age: ${i.lastAudioDataAgeMs} ms`,`Audio frames: ${i.audioFrameCount}`,`Level samples: ${i.levelSampleCount}`,`Maximum peak: ${i.maxPeak}`,`Recorder track state: ${i.trackReadyState}`,`Recorder track enabled: ${i.trackEnabled}`,`Recorder track muted: ${i.trackMuted}`,`Recorder audio context: ${i.audioContextState}`]:[],`Browser: ${navigator.userAgent}`].join(` 162`)}}function eL(e,o){const r=o?.getAudioTracks()[0],i=e instanceof Error?e.name:"UnknownError",c=e instanceof Error?e.message:String(e),l=e instanceof Uw?e.details:void 0;return{message:l?{stop:"The browser did not finish the recorded audio file after you stopped this take.",empty:"The microphone level was visible, but the browser returned an empty recording file.",decode:"The browser captured audio, but could not finish reading the recorded file."}[l.stage]??"This take could not be saved.":"This take could not be saved.",technical:["Voxalike take processing diagnostics",`Stage: ${l?.stage??"unknown"}`,`Error: ${i}: ${c}`,`Recorder mode: ${l?.recorderMode??"unknown"}`,`Recording MIME type: ${l?.mimeType??"unknown"}`,`Recording chunks: ${l?.chunkCount??"unknown"}`,`Recording bytes: ${l?.byteLength??"unknown"}`,`Recorder state: ${l?.recorderState??"unknown"}`,`Stream active: ${o?.active??!1}`,`Track state: ${r?.readyState??"missing"}`,`Track enabled: ${r?.enabled??!1}`,`Track muted: ${r?.muted??!1}`,`Page visibility: ${document.visibilityState}`,`Browser: ${navigator.userAgent}`].join(` 163`)}}function tL(e,o){const r={idle:"Use Mic Check to connect",requesting:"Requesting microphone",ready:"Microphone Ready",denied:"Permission denied","not-found":"Microphone Unavailable",busy:"Microphone may be occupied",overconstrained:"Selected device unavailable",unavailable:"Microphone Unavailable",disconnected:"Device disconnected"};return o&&e!=="ready"?`${r[e]??e}: ${o}`:r[e]??e}function oL(e){return e.includes("Network timeout")?"Network issue. Check your connection and try again.":e.startsWith("Score failed:")||e.startsWith("Next clip failed:")||e.startsWith("Round expired.")?e:""}function YS(e){const o=ea(e);return Number.isFinite(o)?`${o.toFixed(1)} dBFS`:"-inf dBFS"}function rL(e){const o=ea(e);return`${o>-42?"High":o>-52?"Medium":"Low"} (${YS(e)})`}function aL(e){const o=ea(e);return`${o<-34?"Quiet":o>-14?"Loud":"Good"} (${YS(e)})`}function nL(e){return e>=.98?"Clipping":e>=.88?"Hot":"Safe"}function iL(e,o,r){const i=ea(e),c=ea(o);return r>=.98?"Mic is too loud":i>
163-42?"Background noise is high":c<-34?"Voice is quiet":c>-14?"Voice is loud":"Mic sounds ready"}function Id(e){return new Set(e.map(r=>r.metadata.videoPath||r.videoUrl||"").filter(Boolean)).size}function Qm(e){const o=e.reduce((r,i)=>{const c=eh(i.videoStartSeconds),l=eh(i.videoEndSeconds),p=eh(i.duration??i.referenceFeatures?.duration),f=l??(c&&p?c+p:c);return f?Math.max(r,f):r},0);return o>0?Math.round(o*1e3)/1e3:void 0}function eh(e){return Number.isFinite(e)&&e!==void 0&&e>0?e:void 0}function sL(e){return{creator:e.creator,characters:e.characters}}function cL(e,o){return{creator:o?.creator||"",characters:o?.characters||[]}}function ew(e,o){return[o?.creator?o.creator:"",o?.videoDurationSeconds?`Length: ${KS(o.videoDurationSeconds)}`:"",o?.characters?.length?`Characters: ${o.characters.join(", ")}`:""].filter(Boolean).join(" / ")}function KS(e){if(!e||!Number.isFinite(e)||e<=0)return"";const o=Math.round(e),r=Math.floor(o/60),i=o%60;return`${r}:${String(i).padStart(2,"0")}`}function lL(e){return!e.ratingCount||e.ratingAverage===null?"Rating --":`Rating ${e.ratingAverage}/5 (${e.ratingCount})`}function tw(e){const o=Math.max(0,e);return`${o} ${o===1?"download":"downloads"}`}function Nd(e,o){const r=o.map(l=>e[l.id]?.score).filter(l=>!!l);if(!r.length)return;const i=l=>Math.round(r.reduce((p,f)=>p+f[l],0)/r.length),c=AS(i("total"));return{duration:i("duration"),timing:i("timing"),energy:i("energy"),pitch:i("pitch"),total:c,rating:dg(c),notes:[`${r.length}/${o.length} clips scored`,"Full run average"]}}function dL(){return{duration:0,timing:0,energy:0,pitch:0,total:0,rating:dg(0),notes:["No clips scored","Original lines kept"]}}function ow(e){if(e.score)return{...e.score,notes:[...e.score.notes]};const o=e.scoreTotal??0;return{duration:o,timing:o,energy:o,pitch:o,total:o,rating:e.rating??dg(o),notes:["Room take"]}}function uL(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?crypto.randomUUID():`${Date.now()}-${Math.random().toString(36).slice(2,10)}`}function pL(e){const o=[...e];for(let r=o.length-1;r>0;r-=1){const i=Math.floor(Math.random()*(r+1));[o[r],o[i]]=[o[i],o[r]]}return o}function dg(e){return e>=100?"Perfect Dub":e>=95?"Viral Performance":e>=85?"Scene Stealer":e>=75?"Great Take":e>=60?"Solid Dub":e>=40?"Getting There":"Keep Practicing"}function Dd(e){return new Promise((o,r)=>{const i=new FileReader;i.onerror=()=>r(new Error("Could not prepare room playback audio.")),i.onload=()=>o(String(i.result??"")),i.readAsDataURL(e)})}const mL=window.location.pathname==="/"?Promise.all([bL(),aw("/button_hover.wav"),aw("/game_button_hover.wav")]):Promise.resolve();class hL extends m.Component{state={failed:!1};static getDerivedStateFromError(){return{failed:!0}}componentDidCatch(o,r){og("render-error",o,r.componentStack??void 0),!Lh(o)&&console.error("Voxalike render failed",o,r)}render(){return this.state.failed?this.props.fallback:this.props.children}}function gL({children:e}){const{t:o}=Be();return t.jsx(hL,{fallback:t.jsxs("main",{className:"app-error-recovery",translate:"no",children:[t.jsx("h1",{children:o("Page interrupted")}),t.jsx("p",{children:o("Your room and locally saved recordings can be restored after reloading.")}),t.jsx("button",{type:"button",onClick:()=>window.location.reload(),children:o("Reload Voxalike")})]}),children:e})}function fL(){return m.useEffect(()=>{const e=document.documentElement,o=document.getElementById("app-boot-loader");let r,i,c=!1;return Promise.race([mL,new Promise(l=>window.setTimeout(l,5e3))]).then(()=>{c||(i=window.requestAnimationFrame(()=>{e.classList.remove("app-booting","app-load-timeout"),e.classList.add("app-ready"),window.dispatchEvent(new Event("voxalike:app-ready")),r=window.setTimeout(()=>o?.remove(),220)}))}),()=>{c=!0,i!==void 0&&window.cancelAnimationFrame(i),r!==void 0&&window.clearTimeout(r)}},[]),t.jsxs(VT,{children:[t.jsx(xD,{}),null]})}const ug=document.getElementById("root");tM(ug);eM(ug);window.addEventListener("error",e=>{og("error",e.error??e.message),Lh(e.error??e.message)&&e.preventDefault()},!0);window.addEventListener("unhandledrejection",e=>{og("unhandled-rejection",e.reason),Lh(e.reason)&&e.preventDefault()});DC.createRoot(ug).render(t.jsx(m.StrictMode,{children:t.jsx(dj,{children:t.jsx(gL,{children:t.jsx(fL,{})})})}));function bL(){const e=new Image;return e.decoding="async",e.src="/menu-cover.png",e.decode?e.decode().catch(()=>rw(e)):rw(e)}function rw(e){return e.complete?Promise.resolve():new Promise(o=>{e.addEventListener("load",()=>o(),{once:!0}),e.addEventListener("error",()=>o(),{once:!0})})}async function aw(e){try{const o=await fetch(e,{cache:"force-cache"});o.ok&&await o.arrayBuffer()}catch{}}export{Ko as _};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.