@utsp/runtime-client 0.14.0-nightly.20251219144833.1c27cfe → 0.14.0-nightly.20251221141916.9580af2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var F=Object.defineProperty;var oe=Object.getOwnPropertyDescriptor;var se=Object.getOwnPropertyNames;var ae=Object.prototype.hasOwnProperty;var de=(f,t,e)=>t in f?F(f,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):f[t]=e;var y=(f,t)=>F(f,"name",{value:t,configurable:!0});var le=(f,t)=>{for(var e in t)F(f,e,{get:t[e],enumerable:!0})},ce=(f,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of se(t))!ae.call(f,r)&&r!==e&&F(f,r,{get:()=>t[r],enumerable:!(i=oe(t,r))||i.enumerable});return f};var ue=f=>ce(F({},"__esModule",{value:!0}),f);var a=(f,t,e)=>(de(f,typeof t!="symbol"?t+"":t,e),e);var pe={};le(pe,{AudioFeature:()=>G,AudioManager:()=>ne.AudioManager,BaseClientRuntime:()=>P,ClientRuntime:()=>x,InputFeature:()=>L,PostProcessFeature:()=>V,RendererType:()=>q,ScalingMode:()=>re.ScalingMode});module.exports=ue(pe);var J=require("@utsp/core"),B=require("@utsp/render");var q=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(q||{});var $=class ${constructor(t=60){a(this,"frameCount",0);a(this,"fps",0);a(this,"fpsUpdateTime",0);a(this,"lastCoreTime",0);a(this,"lastRenderTime",0);a(this,"lastTotalTime",0);a(this,"coreTimeSamples",[]);a(this,"renderTimeSamples",[]);a(this,"totalTimeSamples",[]);a(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};y($,"PerformanceMonitor");var U=$;var he="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",k={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function N(){let f=atob(he),t=new Uint8Array(f.length);for(let e=0;e<f.length;e++)t[e]=f.charCodeAt(e);return t}y(N,"decodeDefaultAtlas");var Q=class Q{constructor(t,e={}){a(this,"renderer");a(this,"options");this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=N();await this.renderer.setImageFont(e,k.glyphWidth,k.glyphHeight,k.cellWidth,k.cellHeight,k.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let o=y(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let d=`Renderer failed to be ready after ${t*e}ms`;this.log(d),n(new Error(d))}else setTimeout(o,e)},"check");o()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}render(t,e){if(!this.renderer.isReady())return console.warn("[RENDERER MANAGER] Renderer not ready"),!1;let i=t.getRenderState(e);if(!i||i.displays.length===0)return console.warn("[RENDERER MANAGER] No render state or no displays"),!1;let r=i.displays[0];return this.renderer.renderDisplayData(r),!0}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};y(Q,"RendererManager");var E=Q;var W=class W{constructor(t){a(this,"core");a(this,"rendererManager");a(this,"rendererType");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"userId","");a(this,"visibilityChangeHandler");a(this,"tickRate",30);a(this,"accumulatedTime",0);a(this,"FRAME_TIME_MIN",1e3/60);a(this,"lastRenderTimestamp",0);a(this,"rafId",0);a(this,"firstTickDone",!1);a(this,"performanceMonitor");a(this,"renderMode","continuous");a(this,"renderRequested",!1);this.options={application:t.application,container:t.container,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new J.Core({mode:"client",maxUsers:1}),this.core.onPaletteChanged(i=>{this.onCorePaletteChanged(i)}),this.core.onBitmapFontChanged(i=>{this.onCoreBitmapFontChanged(i)}),this.core.onImageFontChanged(i=>{this.onCoreImageFontChanged(i)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler);let e=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new E(e,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new U(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(){if((this.options.renderer??"webgl")==="terminal2d"){this.log("Creating Terminal 2D renderer");let n=new B.Terminal2D(this.options.container,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),n.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),n}this.log("Creating TerminalGL renderer");let i=document.createElement("canvas").getContext("webgl");if(!i)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!i.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(this.options.container instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${this.options.container.tagName}`);let r=new B.TerminalGL(this.options.container,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),r}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();let t=this.rendererManager.getRenderer();t&&"clearOnResizeCallback"in t&&t.clearOnResizeCallback(),this.options.application.destroy&&this.options.application.destroy(),this.rendererManager.destroy(),this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"}),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(o=>{t.recordBytesReceived(o.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(o=>{t.recordBytesReceived(o.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((o,s)=>{let d=this.core.getUser(s);d&&d.recordBytesReceived(o.length)}),this.render(),this.log("Initial render complete")}onUserCreated(t){}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}if(this.tickRate===0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,l),this.rafId=requestAnimationFrame(h=>this.mainLoop(h));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=r;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let s=0,d=0;for(;this.accumulatedTime>=o&&s<5;){let c=performance.now();this.collectAndApplyInput(n),this.options.application.update(this.core,o),this.options.application.updateUser(this.core,n,o),this.processTickOrders(n);let l=n.updateMacros();n.processMacroEvents(l),this.core.endTickSplit().forEach(({static:u,dynamic:p})=>{let m=(u?.length??0)+(p?.length??0);n.recordBytesReceived(m)}),d+=performance.now()-c,this.accumulatedTime-=o,s++}if(s>0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(d,l)}this.rafId=requestAnimationFrame(c=>this.mainLoop(c))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let e=t.getMacroRenderOrders();for(let[i,r]of e){let n=t.getLayerById(i);n&&r.length>0&&n.addTemporaryOrders(r)}}this.rendererManager.render(this.core,this.userId)}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let r=0;r<256;r++){let n=t.get(r);n?e.push({r:n.r,g:n.g,b:n.b,a:n.a}):e.push({r:0,g:0,b:0,a:0})}let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update palette: renderer is null");return}"setPalette"in i&&typeof i.setPalette=="function"&&(i.setPalette(e),this.log("\u2713 Renderer palette updated successfully"))}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let o=0;o<256;o++){let s=e.getGlyph(o);s&&(i.set(o,s),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=this.rendererManager.getRenderer();if(!n){console.warn("[BaseClientRuntime] Cannot update font: renderer is null");return}"setBitmapFont"in n&&typeof n.setBitmapFont=="function"&&(n.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight()),this.log("\u2713 Renderer bitmap font updated successfully"),this.onFontChanged(n))}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update image font: renderer is null");return}"setImageFont"in i&&typeof i.setImageFont=="function"&&(i.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks()),this.log("\u2713 Renderer image font updated successfully"),this.onFontChanged(i))}onFontChanged(t){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(){return this.rendererManager}getUserId(){return this.userId}};y(W,"BaseClientRuntime");var P=W;var v=require("@utsp/core"),A=require("@utsp/render"),X=require("@utsp/types"),C=require("@utsp/input"),_=require("@utsp/network-client"),Z=require("@utsp/audio");var w=require("@utsp/input");var K=class K{constructor(t,e,i,r,n){a(this,"network");a(this,"core");a(this,"rendererManager");a(this,"performanceMonitor");a(this,"options");a(this,"audioManager",null);a(this,"postProcessCallback",null);a(this,"userId","");a(this,"lastReceivedTick",-1);a(this,"connected",!1);a(this,"totalBytesReceived",0);this.network=t,this.core=e,this.rendererManager=i,this.performanceMonitor=r,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.rendererManager.getCanvas();if(i){let g=this.rendererManager.getRenderer()?.getOffsets?.(),S={offsetX:g?.offsetX??0,offsetY:g?.offsetY??0},b=w.InputCollector.collectMousePosition(t,i,this.options.canvasWidth,this.options.canvasHeight,S);e.setMousePosition(b.x,b.y,b.over),e.updateMacroMouse(b.x,b.y,b.isLeftDown),w.InputCollector.collectTouchPositions(t,i,this.options.canvasWidth,this.options.canvasHeight,10,S).forEach(I=>{e.setTouchPosition(I.id,I.x,I.y,I.over)})}let r=e.getInputBindingRegistry(),n=r.getAllAxes(),o=r.getAllButtons(),s=w.InputCollector.collectAxisSources(n,t),d=w.InputCollector.collectButtonSources(o,t),c=w.InputCollector.collectTextInputs(t);c.length>0&&e.setTextInputs(c);let l={};n.forEach(m=>{let g=r.evaluateAxis(m.bindingId,s);l[m.name]=g,e.setAxis(m.name,g)});let h={};o.forEach(m=>{let g=r.evaluateButton(m.bindingId,d);h[m.name]=g,e.setButton(m.name,g)});let u=e.getMouseDisplayInfo(),p=u?{x:u.localX,y:u.localY}:null;this.network.send("input",{timestamp:Date.now(),axes:l,buttons:h,mousePosition:p,textInputs:c})}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.rendererManager.render(this.core,this.userId)):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)?this.log("Input bindings configured"):this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let o=this.core.applyUpdatePacketBuffer(this.userId,i);if(o){console.warn(`[CLIENT] ${e}: ${i.length}B (tick ${n}) - APPLIED`),this.postProcessCallback&&o.postProcessOrders&&o.postProcessOrders.length>0&&this.postProcessCallback(o.postProcessOrders),this.options.debug&&this.debugRenderState();let s=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-s;this.performanceMonitor.recordFrameTiming(0,d),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${d.toFixed(2)}ms)`)}else console.warn(`[CLIENT] Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let i=t.getLayers();console.warn(`[CLIENT] User has ${i.length} layers`),i.forEach((r,n)=>{let o=r.getOrders(),s=r.getStatic();(o.length>0||s)&&console.warn(` Layer ${n}: ${o.length} orders, static=${s}, z=${r.getZOrder()}`)})}let e=this.rendererManager.isReady();console.warn(`[CLIENT] Renderer ready: ${e}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+r.data.length+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromData(i.soundId,i.name,i.data),this.log(`Loaded sound: ${i.name} (${i.data.length} bytes)`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};y(K,"NetworkSync");var O=K;var H=class H extends P{constructor(e){super({application:e.application,container:e.container,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});a(this,"input",null);a(this,"networkSync",null);a(this,"mode");a(this,"localOptions",null);a(this,"autoplayOverlay",null);a(this,"autoplay",!0);a(this,"audioManager",null);a(this,"mobileVibration",null);a(this,"postProcessOverlay",null);a(this,"postProcessOrderCollector",new v.PostProcessOrderCollector);a(this,"localBridgeHandlers",new Map);a(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new v.Core({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r)}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new C.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!0,passive:e.mobileInputConfig?.passive??!1,maxTouches:e.mobileInputConfig?.maxTouches??10}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new _.SocketIOClient({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new O(n,this.core,this.rendererManager,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25})}this.log("ClientRuntime initialized")}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new Z.AudioManager({debug:this.options.debug}),this.mobileVibration=new C.MobileVibration({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new A.AutoplayOverlay(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.postProcessOverlay=new A.PostProcessOverlay(this.options.container),this.log("PostProcessOverlay created");let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync&&(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(i=>{this.applyPostProcessOrders(i)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application))}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.postProcessOverlay=new A.PostProcessOverlay(this.options.container),this.log("PostProcessOverlay created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this);let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync?(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application)):await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let o=r[0].getSize(),s=o.x,d=o.y,c=this.rendererManager.getRenderer(),l,h;if(this.rendererType==="webgl"){let p=c.getGridSize();l=p.cols,h=p.rows}else{let u=c;l=u.getCols(),h=u.getRows()}(l!==s||h!==d)&&(this.log(`Adjusting renderer from ${l}\xD7${h} to match display ${s}\xD7${d}`),c.resize(s,d))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.audioManager&&i.setAudioProcessor(this.audioManager),this.mobileVibration&&i.setMobileVibrationProcessor(this.mobileVibration),this.input){let d=this.input.getGamepad();d&&i.setGamepadVibrationProcessor(d)}if(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"}),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let d=i.flushMacroOrders();i.applyMacroOrders(d)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let d=i.flushPostProcessCommands(),c=this.postProcessOrderCollector.convertCommands(d);this.applyPostProcessOrders(c)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(d=>{i.recordBytesReceived(d.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(d=>{i.recordBytesReceived(d.length)});let o=i.getInputBindingsLoadPacket();o&&i.recordBytesReceived(o.length),this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((d,c)=>{let l=this.core.getUser(c);l&&l.recordBytesReceived(d.length)}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),o=0;for(let s of r)try{if(s.loadType==="file"&&s.data){await e.loadFromData(s.soundId,s.name,s.data);let d=s.data.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded sound: ${s.name} (${s.data.length} bytes)`)}else if(s.loadType==="external"&&s.url){await e.loadFromUrl(s.soundId,s.name,s.url);let d=s.url.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded external sound: ${s.name} from ${s.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${s.name}":`,d)}n&&o>0&&(n.recordBytesReceived(o),this.log(`Simulated ${o} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let o=this.core.getUser(this.userId);if(!o){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}if(this.mode==="local"){if(this.tickRate===0){let l=performance.now();this.render();let h=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,h),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=n;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let d=0,c=0;for(;this.accumulatedTime>=s&&d<5;){let l=performance.now();if(this.collectAndApplyInput(o),this.options.application.update(this.core,s),this.options.application.updateUser(this.core,o,s),o.clearTextInputs(),o.hasAudioConfigCommands()&&o.applyAudioConfigCommands(o.flushAudioConfigCommands()),o.hasSoundCommands()&&o.applyAudioCommands(o.flushSoundCommands()),o.hasMobileVibrationCommands()&&o.applyMobileVibrationCommands(o.flushMobileVibrationCommands()),o.hasGamepadVibrationCommands()&&o.applyGamepadVibrationCommands(o.flushGamepadVibrationCommands()),o.hasPostProcessCommands()){let p=o.flushPostProcessCommands(),m=this.postProcessOrderCollector.convertCommands(p);this.applyPostProcessOrders(m)}if(o.hasPendingMacroOrders()){let p=o.flushMacroOrders();o.applyMacroOrders(p)}let h=o.updateMacros();if(o.processMacroEvents(h),o.hasBridgeMessages()){let p=o.getBridgeMessages();this.dispatchLocalBridgeMessages(p)}this.core.endTickSplit().forEach(({static:p,dynamic:m})=>{let g=(p?.length??0)+(m?.length??0);o.recordBytesReceived(g)}),c+=performance.now()-l,this.accumulatedTime-=s,d++}if(d>0){let l=performance.now();this.render();let h=performance.now()-l;this.performanceMonitor.recordFrameTiming(c,h)}}else{let s=o.updateMacros();o.processMacroEvents(s),this.collectAndSendInput()}this.rafId=requestAnimationFrame(s=>this.mainLoop(s))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let d=e.getDisplays(),c=d.length>0?d[0].getSize():{x:this.options.width,y:this.options.height},l=c.x,h=c.y,p=this.rendererManager.getRenderer()?.getOffsets?.(),m={offsetX:p?.offsetX??0,offsetY:p?.offsetY??0},g=C.InputCollector.collectMousePosition(this.input,i,l,h,m);e.setMousePosition(g.x,g.y,g.over),e.updateMacroMouse(g.x,g.y,g.isLeftDown),C.InputCollector.collectTouchPositions(this.input,i,l,h,10,m).forEach(b=>{e.setTouchPosition(b.id,b.x,b.y,b.over)})}let r=C.InputCollector.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),o=n.getAllAxes(),s=n.getAllButtons();if(o.length>0||s.length>0){let d=C.InputCollector.collectAxisSources(o,this.input),c=C.InputCollector.collectButtonSourcesWithTransitions(s,this.input);o.forEach(l=>{let h=n.evaluateAxis(l.bindingId,d);e.setAxis(l.name,h)}),s.forEach(l=>{let h=new Map,u=new Map,p=new Map;for(let[b,M]of c)h.set(b,M.pressed),u.set(b,M.justPressed),p.set(b,M.justReleased);let m=n.evaluateButton(l.bindingId,h),g=n.evaluateButton(l.bindingId,u),S=n.evaluateButton(l.bindingId,p);e.setButton(l.name,m),e.setButton(`${l.name}_justPressed`,g),e.setButton(`${l.name}_justReleased`,S)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}onFontChanged(e){this.postProcessOverlay&&"syncWithRenderer"in this.postProcessOverlay&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(this.postProcessOverlay.syncWithRenderer(e),this.log("\u2713 PostProcessOverlay synced with renderer dimensions"))}getAudioManager(){return this.audioManager}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e){let i=this.rendererManager?.getRenderer();i&&"setAmbientEffect"in i&&(e.enabled?i.setAmbientEffect({blur:e.blur,scale:e.scale}):i.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){if(!this.postProcessOverlay){this.log("PostProcessOverlay not initialized, skipping post-process orders");return}for(let i of e)switch(i.type){case v.PostProcessOrderType.SetConfig:{let r={};i.scanlines&&(r.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(r.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.postProcessOverlay.setConfig(Object.keys(r).length>0?r:null),r.ambientEffect&&this.applyAmbientEffectConfig(r.ambientEffect);break}case v.PostProcessOrderType.SetScanlines:{this.postProcessOverlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}});break}case v.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffectConfig({enabled:i.enabled,blur:i.blur,scale:i.scale});break}case v.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(i.mode);break}case v.PostProcessOrderType.SetGrid:{this.applyGridConfig(i);break}case v.PostProcessOrderType.SwitchPalette:{this.applySwitchPalette(i);break}}}applyScalingMode(e){let i=this.rendererManager.getRenderer();if(i&&"setScalingMode"in i){let r=(0,X.valueToScalingMode)(e);i.setScalingMode(r),this.log(`Scaling mode set to ${r}`)}}applyGridConfig(e){let i=this.rendererManager.getRenderer();if(i&&"setGrid"in i){let r=e.colorA/255;i.setGrid({enabled:e.enabled,color:`rgba(${e.colorR},${e.colorG},${e.colorB},${r})`,lineWidth:e.lineWidth}),this.log(`Grid ${e.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e){let i=this.core.getPaletteFromSlot(e.slotId);if(!i){console.warn(`[ClientRuntime] Palette slot ${e.slotId} not found`);return}let r=[];for(let o=0;o<256;o++){let s=i.get(o);s?r.push({r:s.r,g:s.g,b:s.b,a:s.a}):r.push({r:0,g:0,b:0,a:0})}let n=this.rendererManager.getRenderer();n&&"setPalette"in n&&(n.setPalette(r),this.log(`Switched to palette slot ${e.slotId}`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,o=this.localBridgeHandlers.get(r);if(o)for(let s of o)try{s(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let s of this.localBridgeWildcardHandlers)try{s(r,n)}catch(d){console.error("[ClientRuntime] Bridge wildcard handler error:",d)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};y(H,"ClientRuntime");var x=H;var R=require("@utsp/input");var Y=class Y{constructor(t={}){a(this,"input");a(this,"mobileVibration");this.input=new R.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!0,passive:t.mobileInputConfig?.passive??!1,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new R.MobileVibration({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let l=R.InputCollector.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(l.x,l.y,l.over),t.updateMacroMouse(l.x,l.y,l.isLeftDown),R.InputCollector.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let o=R.InputCollector.collectTextInputs(this.input);o.length>0&&t.setTextInputs(o);let s=t.getInputBindingRegistry(),d=s.getAllAxes(),c=s.getAllButtons();if(d.length>0||c.length>0){let l=R.InputCollector.collectAxisSources(d,this.input),h=R.InputCollector.collectButtonSourcesWithTransitions(c,this.input);d.forEach(u=>{let p=s.evaluateAxis(u.bindingId,l);t.setAxis(u.name,p)}),c.forEach(u=>{let p=new Map,m=new Map,g=new Map;for(let[I,D]of h)p.set(I,D.pressed),m.set(I,D.justPressed),g.set(I,D.justReleased);let S=s.evaluateButton(u.bindingId,p),b=s.evaluateButton(u.bindingId,m),M=s.evaluateButton(u.bindingId,g);t.setButton(u.name,S),t.setButton(`${u.name}_justPressed`,b),t.setButton(`${u.name}_justReleased`,M)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};y(Y,"InputFeature");var L=Y;var ee=require("@utsp/audio");var j=class j{constructor(t={}){a(this,"audioManager");a(this,"debug");this.debug=t.debug??!1,this.audioManager=new ee.AudioManager({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(o){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,o)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};y(j,"AudioFeature");var G=j;var T=require("@utsp/core"),te=require("@utsp/render"),ie=require("@utsp/types");var z=class z{constructor(t,e={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=e.debug??!1,this.overlay=new te.PostProcessOverlay(t),this.orderCollector=new T.PostProcessOrderCollector}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t)switch(i.type){case T.PostProcessOrderType.SetConfig:{let r={};i.scanlines&&(r.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(r.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(r).length>0?r:null),r.ambientEffect&&this.applyAmbientEffect(e,r.ambientEffect);break}case T.PostProcessOrderType.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}});break}case T.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale});break}case T.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(e,i.mode);break}case T.PostProcessOrderType.SetGrid:{this.applyGridConfig(e,i);break}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyScalingMode(t,e){if(t&&"setScalingMode"in t){let i=(0,ie.valueToScalingMode)(e);t.setScalingMode(i),this.log(`Scaling mode set to ${i}`)}}applyGridConfig(t,e){if(t&&"setGrid"in t){let i=e.colorA/255;t.setGrid({enabled:e.enabled,color:`rgba(${e.colorR},${e.colorG},${e.colorB},${i})`,lineWidth:e.lineWidth}),this.log(`Grid ${e.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};y(z,"PostProcessFeature");var V=z;var re=require("@utsp/render"),ne=require("@utsp/audio");
1
+ "use strict";var E=Object.defineProperty;var de=Object.getOwnPropertyDescriptor;var le=Object.getOwnPropertyNames;var ce=Object.prototype.hasOwnProperty;var he=(y,t,e)=>t in y?E(y,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):y[t]=e;var b=(y,t)=>E(y,"name",{value:t,configurable:!0});var ue=(y,t)=>{for(var e in t)E(y,e,{get:t[e],enumerable:!0})},pe=(y,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let r of le(t))!ce.call(y,r)&&r!==e&&E(y,r,{get:()=>t[r],enumerable:!(i=de(t,r))||i.enumerable});return y};var ge=y=>pe(E({},"__esModule",{value:!0}),y);var a=(y,t,e)=>(he(y,typeof t!="symbol"?t+"":t,e),e);var fe={};ue(fe,{AudioFeature:()=>q,AudioManager:()=>ae.AudioManager,BaseClientRuntime:()=>A,ClientRuntime:()=>D,InputFeature:()=>G,PostProcessFeature:()=>W,RendererType:()=>Q,ScalingMode:()=>oe.ScalingMode});module.exports=ge(fe);var Z=require("@utsp/core"),$=require("@utsp/render");var Q=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(Q||{});var H=class H{constructor(t=60){a(this,"frameCount",0);a(this,"fps",0);a(this,"fpsUpdateTime",0);a(this,"lastCoreTime",0);a(this,"lastRenderTime",0);a(this,"lastTotalTime",0);a(this,"coreTimeSamples",[]);a(this,"renderTimeSamples",[]);a(this,"totalTimeSamples",[]);a(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};b(H,"PerformanceMonitor");var O=H;var me="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",P={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function _(){let y=atob(me),t=new Uint8Array(y.length);for(let e=0;e<y.length;e++)t[e]=y.charCodeAt(e);return t}b(_,"decodeDefaultAtlas");var K=class K{constructor(t,e={}){a(this,"renderer");a(this,"options");this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=_();await this.renderer.setImageFont(e,P.glyphWidth,P.glyphHeight,P.cellWidth,P.cellHeight,P.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let s=b(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let d=`Renderer failed to be ready after ${t*e}ms`;this.log(d),n(new Error(d))}else setTimeout(s,e)},"check");s()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}render(t,e){if(!this.renderer.isReady())return console.warn("[RENDERER MANAGER] Renderer not ready"),!1;let i=t.getRenderState(e);if(!i||i.displays.length===0)return console.warn("[RENDERER MANAGER] No render state or no displays"),!1;let r=i.displays[0];return this.renderer.renderDisplayData(r),!0}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}getAvailableSize(){if(this.renderer.getAvailableSize)return this.renderer.getAvailableSize();let t=this.getCanvas();return{width:t?.clientWidth||t?.width||0,height:t?.clientHeight||t?.height||0}}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};b(K,"RendererManager");var L=K;var z=class z{constructor(t){a(this,"core");a(this,"rendererManager");a(this,"rendererType");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"userId","");a(this,"visibilityChangeHandler");a(this,"tickRate",30);a(this,"accumulatedTime",0);a(this,"FRAME_TIME_MIN",1e3/60);a(this,"lastRenderTimestamp",0);a(this,"rafId",0);a(this,"firstTickDone",!1);a(this,"performanceMonitor");a(this,"renderMode","continuous");a(this,"renderRequested",!1);this.options={application:t.application,container:t.container,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new Z.Core({mode:"client",maxUsers:1}),this.core.onPaletteChanged(i=>{this.onCorePaletteChanged(i)}),this.core.onBitmapFontChanged(i=>{this.onCoreBitmapFontChanged(i)}),this.core.onImageFontChanged(i=>{this.onCoreImageFontChanged(i)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler);let e=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new L(e,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new O(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(){if((this.options.renderer??"webgl")==="terminal2d"){this.log("Creating Terminal 2D renderer");let n=new $.Terminal2D(this.options.container,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),n.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),n}this.log("Creating TerminalGL renderer");let i=document.createElement("canvas").getContext("webgl");if(!i)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!i.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(this.options.container instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${this.options.container.tagName}`);let r=new $.TerminalGL(this.options.container,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),r}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();let t=this.rendererManager.getRenderer();t&&"clearOnResizeCallback"in t&&t.clearOnResizeCallback(),this.options.application.destroy&&this.options.application.destroy(),this.rendererManager.destroy(),this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"}),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(s=>{t.recordBytesReceived(s.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(s=>{t.recordBytesReceived(s.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((s,o)=>{let d=this.core.getUser(o);d&&d.recordBytesReceived(s.length)}),this.render(),this.log("Initial render complete")}onUserCreated(t){}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}if(this.tickRate===0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,l),this.rafId=requestAnimationFrame(p=>this.mainLoop(p));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=r;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let o=0,d=0;for(;this.accumulatedTime>=s&&o<5;){let c=performance.now();this.collectAndApplyInput(n),this.options.application.update(this.core,s),this.options.application.updateUser(this.core,n,s),this.processTickOrders(n);let l=n.updateMacros();n.processMacroEvents(l),this.core.endTickSplit().forEach(({static:u,dynamic:h})=>{let g=(u?.length??0)+(h?.length??0);n.recordBytesReceived(g)}),d+=performance.now()-c,this.accumulatedTime-=s,o++}if(o>0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(d,l)}this.rafId=requestAnimationFrame(c=>this.mainLoop(c))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let e=t.getMacroRenderOrders();for(let[i,r]of e){let n=t.getLayerById(i);n&&r.length>0&&n.addTemporaryOrders(r)}}this.rendererManager.render(this.core,this.userId)}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let r=0;r<256;r++){let n=t.get(r);n?e.push({r:n.r,g:n.g,b:n.b,a:n.a}):e.push({r:0,g:0,b:0,a:0})}let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update palette: renderer is null");return}"setPalette"in i&&typeof i.setPalette=="function"&&(i.setPalette(e),this.log("\u2713 Renderer palette updated successfully"))}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let s=0;s<256;s++){let o=e.getGlyph(s);o&&(i.set(s,o),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=this.rendererManager.getRenderer();if(!n){console.warn("[BaseClientRuntime] Cannot update font: renderer is null");return}"setBitmapFont"in n&&typeof n.setBitmapFont=="function"&&(n.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight()),this.log("\u2713 Renderer bitmap font updated successfully"),this.onFontChanged(n))}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update image font: renderer is null");return}"setImageFont"in i&&typeof i.setImageFont=="function"&&(i.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks()),this.log("\u2713 Renderer image font updated successfully"),this.onFontChanged(i))}onFontChanged(t){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(){return this.rendererManager}getUserId(){return this.userId}};b(z,"BaseClientRuntime");var A=z;var R=require("@utsp/core"),B=require("@utsp/render"),U=require("@utsp/types"),S=require("@utsp/input"),te=require("@utsp/network-client"),ie=require("@utsp/audio");var ee=require("@utsp/core"),F=require("@utsp/input");var Y=class Y{constructor(t,e,i,r,n){a(this,"network");a(this,"core");a(this,"rendererManager");a(this,"performanceMonitor");a(this,"options");a(this,"audioManager",null);a(this,"postProcessCallback",null);a(this,"userId","");a(this,"lastReceivedTick",-1);a(this,"connected",!1);a(this,"totalBytesReceived",0);a(this,"lastSentViewports",new Map);this.network=t,this.core=e,this.rendererManager=i,this.performanceMonitor=r,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.rendererManager.getCanvas();if(i){let C=this.rendererManager.getRenderer()?.getOffsets?.(),w={offsetX:C?.offsetX??0,offsetY:C?.offsetY??0},k=F.InputCollector.collectMousePosition(t,i,this.options.canvasWidth,this.options.canvasHeight,w);e.setMousePosition(k.x,k.y,k.over),e.updateMacroMouse(k.x,k.y,k.isLeftDown),F.InputCollector.collectTouchPositions(t,i,this.options.canvasWidth,this.options.canvasHeight,10,w).forEach(x=>{e.setTouchPosition(x.id,x.x,x.y,x.over)})}let r=e.getInputBindingRegistry(),n=r.getAllAxes(),s=r.getAllButtons(),o=F.InputCollector.collectAxisSources(n,t),d=F.InputCollector.collectButtonSources(s,t),c=F.InputCollector.collectTextInputs(t);c.length>0&&e.setTextInputs(c);let l=n.sort((f,C)=>f.bindingId-C.bindingId),p=new Map;for(let f of l){let C=r.evaluateAxis(f.bindingId,o);p.set(f.bindingId,C),e.setAxis(f.name,C)}let u=s.sort((f,C)=>f.bindingId-C.bindingId),h=new Map;for(let f of u){let C=r.evaluateButton(f.bindingId,d);h.set(f.bindingId,C),e.setButton(f.name,C)}let g=e.getMouseDisplayInfo(),m=e.getIsMouseOnADisplay(),v=this.collectChangedViewports(),I=(0,ee.encodeCompressedInput)(0n,p,h,g?.displayId??0,g?.localX??0,g?.localY??0,m,c,[],v);this.network.send("input",I)}collectChangedViewports(){let t=[],{width:e,height:i}=this.rendererManager.getAvailableSize();if(e===0||i===0)return t;let r=0,n=this.lastSentViewports.get(r);return(!n||n.pixelWidth!==e||n.pixelHeight!==i)&&(t.push({displayId:r,pixelWidth:e,pixelHeight:i}),this.lastSentViewports.set(r,{pixelWidth:e,pixelHeight:i}),this.log(`Viewport changed: display ${r} = ${e}x${i}px`)),t}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.rendererManager.render(this.core,this.userId)):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)?this.log("Input bindings configured"):this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let s=this.core.applyUpdatePacketBuffer(this.userId,i);if(s){console.warn(`[CLIENT] ${e}: ${i.length}B (tick ${n}) - APPLIED`),this.postProcessCallback&&s.postProcessOrders&&s.postProcessOrders.length>0&&this.postProcessCallback(s.postProcessOrders),this.options.debug&&this.debugRenderState();let o=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-o;this.performanceMonitor.recordFrameTiming(0,d),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${d.toFixed(2)}ms)`)}else console.warn(`[CLIENT] Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let i=t.getLayers();console.warn(`[CLIENT] User has ${i.length} layers`),i.forEach((r,n)=>{let s=r.getOrders(),o=r.getStatic();(s.length>0||o)&&console.warn(` Layer ${n}: ${s.length} orders, static=${o}, z=${r.getZOrder()}`)})}let e=this.rendererManager.isReady();console.warn(`[CLIENT] Renderer ready: ${e}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+r.data.length+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromData(i.soundId,i.name,i.data),this.log(`Loaded sound: ${i.name} (${i.data.length} bytes)`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};b(Y,"NetworkSync");var V=Y;var j=class j extends A{constructor(e){super({application:e.application,container:e.container,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});a(this,"input",null);a(this,"networkSync",null);a(this,"mode");a(this,"localOptions",null);a(this,"autoplayOverlay",null);a(this,"autoplay",!0);a(this,"audioManager",null);a(this,"mobileVibration",null);a(this,"postProcessOverlay",null);a(this,"postProcessOrderCollector",new R.PostProcessOrderCollector);a(this,"localBridgeHandlers",new Map);a(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new R.Core({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r)}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new S.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!0,passive:e.mobileInputConfig?.passive??!1,maxTouches:e.mobileInputConfig?.maxTouches??10}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new te.SocketIOClient({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new V(n,this.core,this.rendererManager,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25})}this.log("ClientRuntime initialized")}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new ie.AudioManager({debug:this.options.debug}),this.mobileVibration=new S.MobileVibration({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new B.AutoplayOverlay(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.postProcessOverlay=new B.PostProcessOverlay(this.options.container),this.log("PostProcessOverlay created");let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync&&(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(i=>{this.applyPostProcessOrders(i)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application))}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.postProcessOverlay=new B.PostProcessOverlay(this.options.container),this.log("PostProcessOverlay created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this);let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync?(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application)):await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let s=r[0].getSize(),o=s.x,d=s.y,c=this.rendererManager.getRenderer(),l,p;if(this.rendererType==="webgl"){let h=c.getGridSize();l=h.cols,p=h.rows}else{let u=c;l=u.getCols(),p=u.getRows()}(l!==o||p!==d)&&(this.log(`Adjusting renderer from ${l}\xD7${p} to match display ${o}\xD7${d}`),c.resize(o,d))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.audioManager&&i.setAudioProcessor(this.audioManager),this.mobileVibration&&i.setMobileVibrationProcessor(this.mobileVibration),this.input){let d=this.input.getGamepad();d&&i.setGamepadVibrationProcessor(d)}if(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"}),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let d=i.flushMacroOrders();i.applyMacroOrders(d)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let d=i.flushPostProcessCommands(),c=this.postProcessOrderCollector.convertCommands(d);this.applyPostProcessOrders(c)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(d=>{i.recordBytesReceived(d.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(d=>{i.recordBytesReceived(d.length)});let s=i.getInputBindingsLoadPacket();s&&i.recordBytesReceived(s.length),this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((d,c)=>{let l=this.core.getUser(c);l&&l.recordBytesReceived(d.length)}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),s=0;for(let o of r)try{if(o.loadType==="file"&&o.data){await e.loadFromData(o.soundId,o.name,o.data);let d=o.data.length+o.name.length+10;s+=d,this.log(`\u{1F50A} Loaded sound: ${o.name} (${o.data.length} bytes)`)}else if(o.loadType==="external"&&o.url){await e.loadFromUrl(o.soundId,o.name,o.url);let d=o.url.length+o.name.length+10;s+=d,this.log(`\u{1F50A} Loaded external sound: ${o.name} from ${o.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${o.name}":`,d)}n&&s>0&&(n.recordBytesReceived(s),this.log(`Simulated ${s} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let s=this.core.getUser(this.userId);if(!s){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}if(this.mode==="local"){if(this.tickRate===0){let l=performance.now();this.render();let p=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,p),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=n;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let d=0,c=0;for(;this.accumulatedTime>=o&&d<5;){let l=performance.now();if(this.collectAndApplyInput(s),this.options.application.update(this.core,o),this.options.application.updateUser(this.core,s,o),s.clearTextInputs(),s.hasAudioConfigCommands()&&s.applyAudioConfigCommands(s.flushAudioConfigCommands()),s.hasSoundCommands()&&s.applyAudioCommands(s.flushSoundCommands()),s.hasMobileVibrationCommands()&&s.applyMobileVibrationCommands(s.flushMobileVibrationCommands()),s.hasGamepadVibrationCommands()&&s.applyGamepadVibrationCommands(s.flushGamepadVibrationCommands()),s.hasPostProcessCommands()){let h=s.flushPostProcessCommands(),g=this.postProcessOrderCollector.convertCommands(h);this.applyPostProcessOrders(g)}if(s.hasPendingMacroOrders()){let h=s.flushMacroOrders();s.applyMacroOrders(h)}let p=s.updateMacros();if(s.processMacroEvents(p),s.hasBridgeMessages()){let h=s.getBridgeMessages();this.dispatchLocalBridgeMessages(h)}this.core.endTickSplit().forEach(({static:h,dynamic:g})=>{let m=(h?.length??0)+(g?.length??0);s.recordBytesReceived(m)}),c+=performance.now()-l,this.accumulatedTime-=o,d++}if(d>0){let l=performance.now();this.render();let p=performance.now()-l;this.performanceMonitor.recordFrameTiming(c,p)}}else{let o=s.updateMacros();s.processMacroEvents(o),this.collectAndSendInput()}this.rafId=requestAnimationFrame(o=>this.mainLoop(o))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let d=e.getDisplays(),c=d.length>0?d[0].getSize():{x:this.options.width,y:this.options.height},l=c.x,p=c.y,h=this.rendererManager.getRenderer()?.getOffsets?.(),g={offsetX:h?.offsetX??0,offsetY:h?.offsetY??0},m=S.InputCollector.collectMousePosition(this.input,i,l,p,g);e.setMousePosition(m.x,m.y,m.over),e.updateMacroMouse(m.x,m.y,m.isLeftDown),S.InputCollector.collectTouchPositions(this.input,i,l,p,10,g).forEach(f=>{e.setTouchPosition(f.id,f.x,f.y,f.over)});let I=this.rendererManager.getAvailableSize();e.setDisplayViewport(0,I.width,I.height)}let r=S.InputCollector.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),s=n.getAllAxes(),o=n.getAllButtons();if(s.length>0||o.length>0){let d=S.InputCollector.collectAxisSources(s,this.input),c=S.InputCollector.collectButtonSourcesWithTransitions(o,this.input);s.forEach(l=>{let p=n.evaluateAxis(l.bindingId,d);e.setAxis(l.name,p)}),o.forEach(l=>{let p=new Map,u=new Map,h=new Map;for(let[I,f]of c)p.set(I,f.pressed),u.set(I,f.justPressed),h.set(I,f.justReleased);let g=n.evaluateButton(l.bindingId,p),m=n.evaluateButton(l.bindingId,u),v=n.evaluateButton(l.bindingId,h);e.setButton(l.name,g),e.setButton(`${l.name}_justPressed`,m),e.setButton(`${l.name}_justReleased`,v)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}render(){let e=this.core.getUser(this.userId);if(e){let i=e.getDisplays();if(i.length>0){let r=i[0],n=r.getSize(),s=this.rendererManager.getRenderer();if(s){let o,d,c,l,p=null;if(this.rendererType==="webgl"){let h=s,g=h.getGridSize();o=g.cols,d=g.rows,c=h.getCellWidth(),l=h.getCellHeight(),p=h.getScalingMode()}else{let h=s;o=h.getCols(),d=h.getRows(),c=h.getCellWidth(),l=h.getCellHeight(),p=h.getScalingMode()}if(p===U.ScalingMode.Responsive){let h=this.rendererManager.getAvailableSize(),g=256*256;"getMaxCells"in s&&(g=Math.min(g,s.getMaxCells()));let m=Math.floor(h.width/c),v=Math.floor(h.height/l);if(m=Math.min(256,Math.max(1,m)),v=Math.min(256,Math.max(1,v)),m*v>g){let I=Math.sqrt(g/(m*v));m=Math.max(1,Math.floor(m*I)),v=Math.max(1,Math.floor(v*I)),this.log(`\u26A0\uFE0F Device limit: max ${g} cells, clamping to ${m}\xD7${v}`)}(n.x!==m||n.y!==v)&&(this.log(`\u{1F4D0} Resizing display from ${n.x}\xD7${n.y} to ${m}\xD7${v} (available: ${h.width}\xD7${h.height}px, cell: ${c}\xD7${l}px)`),r.setSize(new U.Vector2(m,v)))}let u=r.getSize();(o!==u.x||d!==u.y)&&(this.log(`\u{1F4D0} Resizing renderer from ${o}\xD7${d} to ${u.x}\xD7${u.y}`),s.resize(u.x,u.y))}}}super.render()}onFontChanged(e){this.postProcessOverlay&&"syncWithRenderer"in this.postProcessOverlay&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(this.postProcessOverlay.syncWithRenderer(e),this.log("\u2713 PostProcessOverlay synced with renderer dimensions"))}getAudioManager(){return this.audioManager}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e){let i=this.rendererManager?.getRenderer();i&&"setAmbientEffect"in i&&(e.enabled?i.setAmbientEffect({blur:e.blur,scale:e.scale}):i.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){if(!this.postProcessOverlay){this.log("PostProcessOverlay not initialized, skipping post-process orders");return}for(let i of e){let r=i.displayId;switch(i.type){case R.PostProcessOrderType.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.postProcessOverlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffectConfig(n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case R.PostProcessOrderType.SetScanlines:{this.postProcessOverlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case R.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffectConfig({enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case R.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(r,i.mode);break}case R.PostProcessOrderType.SetGrid:{this.applyGridConfig(r,i);break}case R.PostProcessOrderType.SwitchPalette:{this.applySwitchPalette(r,i);break}case R.PostProcessOrderType.SetCellSize:{this.applyCellSize(r,i.cellWidth,i.cellHeight);break}}}}applyScalingMode(e,i){let r=this.rendererManager.getRenderer();if(r&&"setScalingMode"in r){let n=(0,U.valueToScalingMode)(i);r.setScalingMode(n),this.log(`[Display ${e}] Scaling mode set to ${n}`)}}applyGridConfig(e,i){let r=this.rendererManager.getRenderer();if(r&&"setGrid"in r){let n=i.colorA/255;r.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${n})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e,i){let r=this.core.getPaletteFromSlot(i.slotId);if(!r){console.warn(`[ClientRuntime] Palette slot ${i.slotId} not found`);return}let n=[];for(let o=0;o<256;o++){let d=r.get(o);d?n.push({r:d.r,g:d.g,b:d.b,a:d.a}):n.push({r:0,g:0,b:0,a:0})}let s=this.rendererManager.getRenderer();s&&"setPalette"in s&&(s.setPalette(n),this.log(`[Display ${e}] Switched to palette slot ${i.slotId}`))}applyCellSize(e,i,r){let n=this.rendererManager.getRenderer();n&&"setCellSize"in n&&(n.setCellSize(i,r),this.log(`[Display ${e}] Cell size set to ${i}\xD7${r}px`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,s=this.localBridgeHandlers.get(r);if(s)for(let o of s)try{o(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let o of this.localBridgeWildcardHandlers)try{o(r,n)}catch(d){console.error("[ClientRuntime] Bridge wildcard handler error:",d)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};b(j,"ClientRuntime");var D=j;var T=require("@utsp/input");var N=class N{constructor(t={}){a(this,"input");a(this,"mobileVibration");this.input=new T.UnifiedInputRouter({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!0,passive:t.mobileInputConfig?.passive??!1,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new T.MobileVibration({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let l=T.InputCollector.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(l.x,l.y,l.over),t.updateMacroMouse(l.x,l.y,l.isLeftDown),T.InputCollector.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let s=T.InputCollector.collectTextInputs(this.input);s.length>0&&t.setTextInputs(s);let o=t.getInputBindingRegistry(),d=o.getAllAxes(),c=o.getAllButtons();if(d.length>0||c.length>0){let l=T.InputCollector.collectAxisSources(d,this.input),p=T.InputCollector.collectButtonSourcesWithTransitions(c,this.input);d.forEach(u=>{let h=o.evaluateAxis(u.bindingId,l);t.setAxis(u.name,h)}),c.forEach(u=>{let h=new Map,g=new Map,m=new Map;for(let[C,w]of p)h.set(C,w.pressed),g.set(C,w.justPressed),m.set(C,w.justReleased);let v=o.evaluateButton(u.bindingId,h),I=o.evaluateButton(u.bindingId,g),f=o.evaluateButton(u.bindingId,m);t.setButton(u.name,v),t.setButton(`${u.name}_justPressed`,I),t.setButton(`${u.name}_justReleased`,f)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};b(N,"InputFeature");var G=N;var re=require("@utsp/audio");var J=class J{constructor(t={}){a(this,"audioManager");a(this,"debug");this.debug=t.debug??!1,this.audioManager=new re.AudioManager({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(s){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,s)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};b(J,"AudioFeature");var q=J;var M=require("@utsp/core"),ne=require("@utsp/render"),se=require("@utsp/types");var X=class X{constructor(t,e={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=e.debug??!1,this.overlay=new ne.PostProcessOverlay(t),this.orderCollector=new M.PostProcessOrderCollector}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t){let r=i.displayId;switch(i.type){case M.PostProcessOrderType.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffect(e,n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case M.PostProcessOrderType.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case M.PostProcessOrderType.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case M.PostProcessOrderType.SetScalingMode:{this.applyScalingMode(e,r,i.mode);break}case M.PostProcessOrderType.SetGrid:{this.applyGridConfig(e,r,i);break}}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1))}applyScalingMode(t,e,i){if(t&&"setScalingMode"in t){let r=(0,se.valueToScalingMode)(i);t.setScalingMode(r),this.log(`[Display ${e}] Scaling mode set to ${r}`)}}applyGridConfig(t,e,i){if(t&&"setGrid"in t){let r=i.colorA/255;t.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${r})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};b(X,"PostProcessFeature");var W=X;var oe=require("@utsp/render"),ae=require("@utsp/audio");
package/dist/index.d.ts CHANGED
@@ -362,6 +362,14 @@ type ConcreteRenderer = IRenderer & {
362
362
  * Enable ImageData rendering optimization (Terminal2D specific, optional)
363
363
  */
364
364
  setImageDataRendering?: (enabled: boolean) => void;
365
+ /**
366
+ * Get the available size from the parent container (TerminalGL specific, optional).
367
+ * This is the actual pixel space available for rendering.
368
+ */
369
+ getAvailableSize?: () => {
370
+ width: number;
371
+ height: number;
372
+ };
365
373
  };
366
374
  /**
367
375
  * Manages renderer lifecycle and operations
@@ -471,6 +479,23 @@ declare class RendererManager {
471
479
  * ```
472
480
  */
473
481
  getCanvas(): HTMLCanvasElement | null;
482
+ /**
483
+ * Get the available size from the parent container.
484
+ * This is the actual pixel space available for rendering.
485
+ * Falls back to canvas size if the renderer doesn't support this method.
486
+ *
487
+ * @returns Object with width and height in pixels
488
+ *
489
+ * @example
490
+ * ```typescript
491
+ * const { width, height } = manager.getAvailableSize();
492
+ * const maxCols = Math.floor(width / cellWidth);
493
+ * ```
494
+ */
495
+ getAvailableSize(): {
496
+ width: number;
497
+ height: number;
498
+ };
474
499
  /**
475
500
  * Check if renderer is ready
476
501
  *
@@ -937,6 +962,16 @@ declare class ClientRuntime extends BaseClientRuntime {
937
962
  */
938
963
  protected collectAndApplyInput(user: User): void;
939
964
  private collectAndSendInput;
965
+ /**
966
+ * Override render to sync renderer size with display before rendering
967
+ *
968
+ * This is critical for responsive display sizing - when the application
969
+ * calls display.setSize(), the renderer must be resized accordingly.
970
+ *
971
+ * In Responsive mode, this also automatically calculates the optimal
972
+ * cols/rows based on the available pixel space from the parent container.
973
+ */
974
+ protected render(): void;
940
975
  /**
941
976
  * Override onFontChanged to sync PostProcessOverlay
942
977
  */
@@ -959,11 +994,16 @@ declare class ClientRuntime extends BaseClientRuntime {
959
994
  private applyAmbientEffectConfig;
960
995
  /**
961
996
  * Apply post-process orders
997
+ *
998
+ * Note: Each order contains a displayId indicating which display it targets.
999
+ * Currently the client has a single renderer, so all orders are applied to it.
1000
+ * A future multi-display implementation would route orders to the correct renderer.
962
1001
  */
963
1002
  private applyPostProcessOrders;
964
1003
  private applyScalingMode;
965
1004
  private applyGridConfig;
966
1005
  private applySwitchPalette;
1006
+ private applyCellSize;
967
1007
  private patternFromType;
968
1008
  private dispatchLocalBridgeMessages;
969
1009
  sendBridge(channel: string, data: unknown): void;
@@ -1124,6 +1164,9 @@ declare class PostProcessFeature {
1124
1164
  processCommands(user: User, renderer: any): void;
1125
1165
  /**
1126
1166
  * Apply post-process orders (used by both local and network modes)
1167
+ *
1168
+ * Each order contains a displayId indicating which display it targets.
1169
+ * Currently applies to the single renderer, ready for future multi-display support.
1127
1170
  */
1128
1171
  applyOrders(orders: AnyPostProcessOrder[], renderer: any): void;
1129
1172
  private applyAmbientEffect;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- var H=Object.defineProperty;var N=(b,t,e)=>t in b?H(b,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):b[t]=e;var f=(b,t)=>H(b,"name",{value:t,configurable:!0});var a=(b,t,e)=>(N(b,typeof t!="symbol"?t+"":t,e),e);import{Core as X}from"@utsp/core";import{TerminalGL as _,Terminal2D as Z}from"@utsp/render";var Y=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(Y||{});var B=class B{constructor(t=60){a(this,"frameCount",0);a(this,"fps",0);a(this,"fpsUpdateTime",0);a(this,"lastCoreTime",0);a(this,"lastRenderTime",0);a(this,"lastTotalTime",0);a(this,"coreTimeSamples",[]);a(this,"renderTimeSamples",[]);a(this,"totalTimeSamples",[]);a(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};f(B,"PerformanceMonitor");var F=B;var J="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",I={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function j(){let b=atob(J),t=new Uint8Array(b.length);for(let e=0;e<b.length;e++)t[e]=b.charCodeAt(e);return t}f(j,"decodeDefaultAtlas");var O=class O{constructor(t,e={}){a(this,"renderer");a(this,"options");this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=j();await this.renderer.setImageFont(e,I.glyphWidth,I.glyphHeight,I.cellWidth,I.cellHeight,I.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let o=f(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let d=`Renderer failed to be ready after ${t*e}ms`;this.log(d),n(new Error(d))}else setTimeout(o,e)},"check");o()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}render(t,e){if(!this.renderer.isReady())return console.warn("[RENDERER MANAGER] Renderer not ready"),!1;let i=t.getRenderState(e);if(!i||i.displays.length===0)return console.warn("[RENDERER MANAGER] No render state or no displays"),!1;let r=i.displays[0];return this.renderer.renderDisplayData(r),!0}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};f(O,"RendererManager");var A=O;var x=class x{constructor(t){a(this,"core");a(this,"rendererManager");a(this,"rendererType");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"userId","");a(this,"visibilityChangeHandler");a(this,"tickRate",30);a(this,"accumulatedTime",0);a(this,"FRAME_TIME_MIN",1e3/60);a(this,"lastRenderTimestamp",0);a(this,"rafId",0);a(this,"firstTickDone",!1);a(this,"performanceMonitor");a(this,"renderMode","continuous");a(this,"renderRequested",!1);this.options={application:t.application,container:t.container,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new X({mode:"client",maxUsers:1}),this.core.onPaletteChanged(i=>{this.onCorePaletteChanged(i)}),this.core.onBitmapFontChanged(i=>{this.onCoreBitmapFontChanged(i)}),this.core.onImageFontChanged(i=>{this.onCoreImageFontChanged(i)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler);let e=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new A(e,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new F(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(){if((this.options.renderer??"webgl")==="terminal2d"){this.log("Creating Terminal 2D renderer");let n=new Z(this.options.container,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),n.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),n}this.log("Creating TerminalGL renderer");let i=document.createElement("canvas").getContext("webgl");if(!i)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!i.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(this.options.container instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${this.options.container.tagName}`);let r=new _(this.options.container,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),r}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();let t=this.rendererManager.getRenderer();t&&"clearOnResizeCallback"in t&&t.clearOnResizeCallback(),this.options.application.destroy&&this.options.application.destroy(),this.rendererManager.destroy(),this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"}),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(o=>{t.recordBytesReceived(o.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(o=>{t.recordBytesReceived(o.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((o,s)=>{let d=this.core.getUser(s);d&&d.recordBytesReceived(o.length)}),this.render(),this.log("Initial render complete")}onUserCreated(t){}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}if(this.tickRate===0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,l),this.rafId=requestAnimationFrame(h=>this.mainLoop(h));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=r;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let s=0,d=0;for(;this.accumulatedTime>=o&&s<5;){let c=performance.now();this.collectAndApplyInput(n),this.options.application.update(this.core,o),this.options.application.updateUser(this.core,n,o),this.processTickOrders(n);let l=n.updateMacros();n.processMacroEvents(l),this.core.endTickSplit().forEach(({static:u,dynamic:p})=>{let m=(u?.length??0)+(p?.length??0);n.recordBytesReceived(m)}),d+=performance.now()-c,this.accumulatedTime-=o,s++}if(s>0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(d,l)}this.rafId=requestAnimationFrame(c=>this.mainLoop(c))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let e=t.getMacroRenderOrders();for(let[i,r]of e){let n=t.getLayerById(i);n&&r.length>0&&n.addTemporaryOrders(r)}}this.rendererManager.render(this.core,this.userId)}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let r=0;r<256;r++){let n=t.get(r);n?e.push({r:n.r,g:n.g,b:n.b,a:n.a}):e.push({r:0,g:0,b:0,a:0})}let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update palette: renderer is null");return}"setPalette"in i&&typeof i.setPalette=="function"&&(i.setPalette(e),this.log("\u2713 Renderer palette updated successfully"))}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let o=0;o<256;o++){let s=e.getGlyph(o);s&&(i.set(o,s),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=this.rendererManager.getRenderer();if(!n){console.warn("[BaseClientRuntime] Cannot update font: renderer is null");return}"setBitmapFont"in n&&typeof n.setBitmapFont=="function"&&(n.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight()),this.log("\u2713 Renderer bitmap font updated successfully"),this.onFontChanged(n))}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update image font: renderer is null");return}"setImageFont"in i&&typeof i.setImageFont=="function"&&(i.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks()),this.log("\u2713 Renderer image font updated successfully"),this.onFontChanged(i))}onFontChanged(t){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(){return this.rendererManager}getUserId(){return this.userId}};f(x,"BaseClientRuntime");var S=x;import{Core as ee,PostProcessOrderType as T,PostProcessOrderCollector as te}from"@utsp/core";import{AutoplayOverlay as ie,PostProcessOverlay as z}from"@utsp/render";import{valueToScalingMode as re}from"@utsp/types";import{UnifiedInputRouter as ne,InputCollector as k,MobileVibration as oe}from"@utsp/input";import{SocketIOClient as se}from"@utsp/network-client";import{AudioManager as ae}from"@utsp/audio";import{InputCollector as M}from"@utsp/input";var L=class L{constructor(t,e,i,r,n){a(this,"network");a(this,"core");a(this,"rendererManager");a(this,"performanceMonitor");a(this,"options");a(this,"audioManager",null);a(this,"postProcessCallback",null);a(this,"userId","");a(this,"lastReceivedTick",-1);a(this,"connected",!1);a(this,"totalBytesReceived",0);this.network=t,this.core=e,this.rendererManager=i,this.performanceMonitor=r,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.rendererManager.getCanvas();if(i){let g=this.rendererManager.getRenderer()?.getOffsets?.(),C={offsetX:g?.offsetX??0,offsetY:g?.offsetY??0},y=M.collectMousePosition(t,i,this.options.canvasWidth,this.options.canvasHeight,C);e.setMousePosition(y.x,y.y,y.over),e.updateMacroMouse(y.x,y.y,y.isLeftDown),M.collectTouchPositions(t,i,this.options.canvasWidth,this.options.canvasHeight,10,C).forEach(v=>{e.setTouchPosition(v.id,v.x,v.y,v.over)})}let r=e.getInputBindingRegistry(),n=r.getAllAxes(),o=r.getAllButtons(),s=M.collectAxisSources(n,t),d=M.collectButtonSources(o,t),c=M.collectTextInputs(t);c.length>0&&e.setTextInputs(c);let l={};n.forEach(m=>{let g=r.evaluateAxis(m.bindingId,s);l[m.name]=g,e.setAxis(m.name,g)});let h={};o.forEach(m=>{let g=r.evaluateButton(m.bindingId,d);h[m.name]=g,e.setButton(m.name,g)});let u=e.getMouseDisplayInfo(),p=u?{x:u.localX,y:u.localY}:null;this.network.send("input",{timestamp:Date.now(),axes:l,buttons:h,mousePosition:p,textInputs:c})}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.rendererManager.render(this.core,this.userId)):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)?this.log("Input bindings configured"):this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let o=this.core.applyUpdatePacketBuffer(this.userId,i);if(o){console.warn(`[CLIENT] ${e}: ${i.length}B (tick ${n}) - APPLIED`),this.postProcessCallback&&o.postProcessOrders&&o.postProcessOrders.length>0&&this.postProcessCallback(o.postProcessOrders),this.options.debug&&this.debugRenderState();let s=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-s;this.performanceMonitor.recordFrameTiming(0,d),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${d.toFixed(2)}ms)`)}else console.warn(`[CLIENT] Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let i=t.getLayers();console.warn(`[CLIENT] User has ${i.length} layers`),i.forEach((r,n)=>{let o=r.getOrders(),s=r.getStatic();(o.length>0||s)&&console.warn(` Layer ${n}: ${o.length} orders, static=${s}, z=${r.getZOrder()}`)})}let e=this.rendererManager.isReady();console.warn(`[CLIENT] Renderer ready: ${e}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+r.data.length+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromData(i.soundId,i.name,i.data),this.log(`Loaded sound: ${i.name} (${i.data.length} bytes)`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};f(L,"NetworkSync");var U=L;var V=class V extends S{constructor(e){super({application:e.application,container:e.container,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});a(this,"input",null);a(this,"networkSync",null);a(this,"mode");a(this,"localOptions",null);a(this,"autoplayOverlay",null);a(this,"autoplay",!0);a(this,"audioManager",null);a(this,"mobileVibration",null);a(this,"postProcessOverlay",null);a(this,"postProcessOrderCollector",new te);a(this,"localBridgeHandlers",new Map);a(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new ee({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r)}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new ne({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!0,passive:e.mobileInputConfig?.passive??!1,maxTouches:e.mobileInputConfig?.maxTouches??10}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new se({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new U(n,this.core,this.rendererManager,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25})}this.log("ClientRuntime initialized")}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new ae({debug:this.options.debug}),this.mobileVibration=new oe({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new ie(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.postProcessOverlay=new z(this.options.container),this.log("PostProcessOverlay created");let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync&&(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(i=>{this.applyPostProcessOrders(i)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application))}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.postProcessOverlay=new z(this.options.container),this.log("PostProcessOverlay created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this);let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync?(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application)):await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let o=r[0].getSize(),s=o.x,d=o.y,c=this.rendererManager.getRenderer(),l,h;if(this.rendererType==="webgl"){let p=c.getGridSize();l=p.cols,h=p.rows}else{let u=c;l=u.getCols(),h=u.getRows()}(l!==s||h!==d)&&(this.log(`Adjusting renderer from ${l}\xD7${h} to match display ${s}\xD7${d}`),c.resize(s,d))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.audioManager&&i.setAudioProcessor(this.audioManager),this.mobileVibration&&i.setMobileVibrationProcessor(this.mobileVibration),this.input){let d=this.input.getGamepad();d&&i.setGamepadVibrationProcessor(d)}if(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"}),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let d=i.flushMacroOrders();i.applyMacroOrders(d)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let d=i.flushPostProcessCommands(),c=this.postProcessOrderCollector.convertCommands(d);this.applyPostProcessOrders(c)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(d=>{i.recordBytesReceived(d.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(d=>{i.recordBytesReceived(d.length)});let o=i.getInputBindingsLoadPacket();o&&i.recordBytesReceived(o.length),this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((d,c)=>{let l=this.core.getUser(c);l&&l.recordBytesReceived(d.length)}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),o=0;for(let s of r)try{if(s.loadType==="file"&&s.data){await e.loadFromData(s.soundId,s.name,s.data);let d=s.data.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded sound: ${s.name} (${s.data.length} bytes)`)}else if(s.loadType==="external"&&s.url){await e.loadFromUrl(s.soundId,s.name,s.url);let d=s.url.length+s.name.length+10;o+=d,this.log(`\u{1F50A} Loaded external sound: ${s.name} from ${s.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${s.name}":`,d)}n&&o>0&&(n.recordBytesReceived(o),this.log(`Simulated ${o} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let o=this.core.getUser(this.userId);if(!o){this.rafId=requestAnimationFrame(s=>this.mainLoop(s));return}if(this.mode==="local"){if(this.tickRate===0){let l=performance.now();this.render();let h=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,h),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=n;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let d=0,c=0;for(;this.accumulatedTime>=s&&d<5;){let l=performance.now();if(this.collectAndApplyInput(o),this.options.application.update(this.core,s),this.options.application.updateUser(this.core,o,s),o.clearTextInputs(),o.hasAudioConfigCommands()&&o.applyAudioConfigCommands(o.flushAudioConfigCommands()),o.hasSoundCommands()&&o.applyAudioCommands(o.flushSoundCommands()),o.hasMobileVibrationCommands()&&o.applyMobileVibrationCommands(o.flushMobileVibrationCommands()),o.hasGamepadVibrationCommands()&&o.applyGamepadVibrationCommands(o.flushGamepadVibrationCommands()),o.hasPostProcessCommands()){let p=o.flushPostProcessCommands(),m=this.postProcessOrderCollector.convertCommands(p);this.applyPostProcessOrders(m)}if(o.hasPendingMacroOrders()){let p=o.flushMacroOrders();o.applyMacroOrders(p)}let h=o.updateMacros();if(o.processMacroEvents(h),o.hasBridgeMessages()){let p=o.getBridgeMessages();this.dispatchLocalBridgeMessages(p)}this.core.endTickSplit().forEach(({static:p,dynamic:m})=>{let g=(p?.length??0)+(m?.length??0);o.recordBytesReceived(g)}),c+=performance.now()-l,this.accumulatedTime-=s,d++}if(d>0){let l=performance.now();this.render();let h=performance.now()-l;this.performanceMonitor.recordFrameTiming(c,h)}}else{let s=o.updateMacros();o.processMacroEvents(s),this.collectAndSendInput()}this.rafId=requestAnimationFrame(s=>this.mainLoop(s))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let d=e.getDisplays(),c=d.length>0?d[0].getSize():{x:this.options.width,y:this.options.height},l=c.x,h=c.y,p=this.rendererManager.getRenderer()?.getOffsets?.(),m={offsetX:p?.offsetX??0,offsetY:p?.offsetY??0},g=k.collectMousePosition(this.input,i,l,h,m);e.setMousePosition(g.x,g.y,g.over),e.updateMacroMouse(g.x,g.y,g.isLeftDown),k.collectTouchPositions(this.input,i,l,h,10,m).forEach(y=>{e.setTouchPosition(y.id,y.x,y.y,y.over)})}let r=k.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),o=n.getAllAxes(),s=n.getAllButtons();if(o.length>0||s.length>0){let d=k.collectAxisSources(o,this.input),c=k.collectButtonSourcesWithTransitions(s,this.input);o.forEach(l=>{let h=n.evaluateAxis(l.bindingId,d);e.setAxis(l.name,h)}),s.forEach(l=>{let h=new Map,u=new Map,p=new Map;for(let[y,R]of c)h.set(y,R.pressed),u.set(y,R.justPressed),p.set(y,R.justReleased);let m=n.evaluateButton(l.bindingId,h),g=n.evaluateButton(l.bindingId,u),C=n.evaluateButton(l.bindingId,p);e.setButton(l.name,m),e.setButton(`${l.name}_justPressed`,g),e.setButton(`${l.name}_justReleased`,C)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}onFontChanged(e){this.postProcessOverlay&&"syncWithRenderer"in this.postProcessOverlay&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(this.postProcessOverlay.syncWithRenderer(e),this.log("\u2713 PostProcessOverlay synced with renderer dimensions"))}getAudioManager(){return this.audioManager}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e){let i=this.rendererManager?.getRenderer();i&&"setAmbientEffect"in i&&(e.enabled?i.setAmbientEffect({blur:e.blur,scale:e.scale}):i.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){if(!this.postProcessOverlay){this.log("PostProcessOverlay not initialized, skipping post-process orders");return}for(let i of e)switch(i.type){case T.SetConfig:{let r={};i.scanlines&&(r.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(r.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.postProcessOverlay.setConfig(Object.keys(r).length>0?r:null),r.ambientEffect&&this.applyAmbientEffectConfig(r.ambientEffect);break}case T.SetScanlines:{this.postProcessOverlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}});break}case T.SetAmbientEffect:{this.applyAmbientEffectConfig({enabled:i.enabled,blur:i.blur,scale:i.scale});break}case T.SetScalingMode:{this.applyScalingMode(i.mode);break}case T.SetGrid:{this.applyGridConfig(i);break}case T.SwitchPalette:{this.applySwitchPalette(i);break}}}applyScalingMode(e){let i=this.rendererManager.getRenderer();if(i&&"setScalingMode"in i){let r=re(e);i.setScalingMode(r),this.log(`Scaling mode set to ${r}`)}}applyGridConfig(e){let i=this.rendererManager.getRenderer();if(i&&"setGrid"in i){let r=e.colorA/255;i.setGrid({enabled:e.enabled,color:`rgba(${e.colorR},${e.colorG},${e.colorB},${r})`,lineWidth:e.lineWidth}),this.log(`Grid ${e.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e){let i=this.core.getPaletteFromSlot(e.slotId);if(!i){console.warn(`[ClientRuntime] Palette slot ${e.slotId} not found`);return}let r=[];for(let o=0;o<256;o++){let s=i.get(o);s?r.push({r:s.r,g:s.g,b:s.b,a:s.a}):r.push({r:0,g:0,b:0,a:0})}let n=this.rendererManager.getRenderer();n&&"setPalette"in n&&(n.setPalette(r),this.log(`Switched to palette slot ${e.slotId}`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,o=this.localBridgeHandlers.get(r);if(o)for(let s of o)try{s(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let s of this.localBridgeWildcardHandlers)try{s(r,n)}catch(d){console.error("[ClientRuntime] Bridge wildcard handler error:",d)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};f(V,"ClientRuntime");var G=V;import{UnifiedInputRouter as de,InputCollector as P,MobileVibration as le}from"@utsp/input";var q=class q{constructor(t={}){a(this,"input");a(this,"mobileVibration");this.input=new de({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!0,passive:t.mobileInputConfig?.passive??!1,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new le({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let l=P.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(l.x,l.y,l.over),t.updateMacroMouse(l.x,l.y,l.isLeftDown),P.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let o=P.collectTextInputs(this.input);o.length>0&&t.setTextInputs(o);let s=t.getInputBindingRegistry(),d=s.getAllAxes(),c=s.getAllButtons();if(d.length>0||c.length>0){let l=P.collectAxisSources(d,this.input),h=P.collectButtonSourcesWithTransitions(c,this.input);d.forEach(u=>{let p=s.evaluateAxis(u.bindingId,l);t.setAxis(u.name,p)}),c.forEach(u=>{let p=new Map,m=new Map,g=new Map;for(let[v,E]of h)p.set(v,E.pressed),m.set(v,E.justPressed),g.set(v,E.justReleased);let C=s.evaluateButton(u.bindingId,p),y=s.evaluateButton(u.bindingId,m),R=s.evaluateButton(u.bindingId,g);t.setButton(u.name,C),t.setButton(`${u.name}_justPressed`,y),t.setButton(`${u.name}_justReleased`,R)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};f(q,"InputFeature");var D=q;import{AudioManager as ce}from"@utsp/audio";var Q=class Q{constructor(t={}){a(this,"audioManager");a(this,"debug");this.debug=t.debug??!1,this.audioManager=new ce({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(o){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,o)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};f(Q,"AudioFeature");var $=Q;import{PostProcessOrderCollector as ue,PostProcessOrderType as w}from"@utsp/core";import{PostProcessOverlay as he}from"@utsp/render";import{valueToScalingMode as pe}from"@utsp/types";var K=class K{constructor(t,e={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=e.debug??!1,this.overlay=new he(t),this.orderCollector=new ue}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t)switch(i.type){case w.SetConfig:{let r={};i.scanlines&&(r.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(r.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(r).length>0?r:null),r.ambientEffect&&this.applyAmbientEffect(e,r.ambientEffect);break}case w.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}});break}case w.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale});break}case w.SetScalingMode:{this.applyScalingMode(e,i.mode);break}case w.SetGrid:{this.applyGridConfig(e,i);break}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyScalingMode(t,e){if(t&&"setScalingMode"in t){let i=pe(e);t.setScalingMode(i),this.log(`Scaling mode set to ${i}`)}}applyGridConfig(t,e){if(t&&"setGrid"in t){let i=e.colorA/255;t.setGrid({enabled:e.enabled,color:`rgba(${e.colorR},${e.colorG},${e.colorB},${i})`,lineWidth:e.lineWidth}),this.log(`Grid ${e.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};f(K,"PostProcessFeature");var W=K;import{ScalingMode as ut}from"@utsp/render";import{AudioManager as pt}from"@utsp/audio";export{$ as AudioFeature,pt as AudioManager,S as BaseClientRuntime,G as ClientRuntime,D as InputFeature,W as PostProcessFeature,Y as RendererType,ut as ScalingMode};
1
+ var Y=Object.defineProperty;var X=(I,t,e)=>t in I?Y(I,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):I[t]=e;var y=(I,t)=>Y(I,"name",{value:t,configurable:!0});var a=(I,t,e)=>(X(I,typeof t!="symbol"?t+"":t,e),e);import{Core as Z}from"@utsp/core";import{TerminalGL as ee,Terminal2D as te}from"@utsp/render";var j=(e=>(e.TerminalGL="webgl",e.Terminal2D="terminal2d",e))(j||{});var O=class O{constructor(t=60){a(this,"frameCount",0);a(this,"fps",0);a(this,"fpsUpdateTime",0);a(this,"lastCoreTime",0);a(this,"lastRenderTime",0);a(this,"lastTotalTime",0);a(this,"coreTimeSamples",[]);a(this,"renderTimeSamples",[]);a(this,"totalTimeSamples",[]);a(this,"maxSamples");if(t<=0)throw new Error("maxSamples must be positive");this.maxSamples=t}startTracking(t){this.fpsUpdateTime=t,this.frameCount=0,this.fps=0}updateFPS(t){this.frameCount++,t-this.fpsUpdateTime>=1e3&&(this.fps=Math.round(this.frameCount*1e3/(t-this.fpsUpdateTime)),this.frameCount=0,this.fpsUpdateTime=t)}recordFrameTiming(t,e){let i=t+e;this.lastCoreTime=t,this.lastRenderTime=e,this.lastTotalTime=i,this.coreTimeSamples.push(t),this.renderTimeSamples.push(e),this.totalTimeSamples.push(i),this.coreTimeSamples.length>this.maxSamples&&(this.coreTimeSamples.shift(),this.renderTimeSamples.shift(),this.totalTimeSamples.shift())}getFPS(){return this.fps}getLastFrameTiming(){if(!(this.lastCoreTime===0&&this.lastRenderTime===0))return{coreTime:this.lastCoreTime,renderTime:this.lastRenderTime,totalTime:this.lastTotalTime}}getAverageFrameTiming(){if(this.coreTimeSamples.length!==0)return{coreTime:this.average(this.coreTimeSamples),renderTime:this.average(this.renderTimeSamples),totalTime:this.average(this.totalTimeSamples)}}getStats(){return{fps:this.fps,lastFrame:this.getLastFrameTiming(),avgFrame:this.getAverageFrameTiming(),sampleCount:this.coreTimeSamples.length,maxSamples:this.maxSamples}}reset(){this.frameCount=0,this.fps=0,this.fpsUpdateTime=0,this.lastCoreTime=0,this.lastRenderTime=0,this.lastTotalTime=0,this.coreTimeSamples=[],this.renderTimeSamples=[],this.totalTimeSamples=[]}average(t){return t.length===0?0:t.reduce((e,i)=>e+i,0)/t.length}};y(O,"PerformanceMonitor");var E=O;var _="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",M={glyphWidth:8,glyphHeight:8,cellWidth:8,cellHeight:8,atlasBlocks:1,atlasWidth:128,atlasHeight:128};function N(){let I=atob(_),t=new Uint8Array(I.length);for(let e=0;e<I.length;e++)t[e]=I.charCodeAt(e);return t}y(N,"decodeDefaultAtlas");var L=class L{constructor(t,e={}){a(this,"renderer");a(this,"options");this.renderer=t,this.options={debug:e.debug??!1,useImageDataRendering:e.useImageDataRendering??!1}}async initialize(t){if("setImageDataRendering"in this.renderer&&this.options.useImageDataRendering&&(this.renderer.setImageDataRendering(!0),this.log("ImageData rendering enabled")),"setImageFont"in this.renderer&&typeof this.renderer.setImageFont=="function"){this.log("Loading default 8x8 font atlas...");let e=N();await this.renderer.setImageFont(e,M.glyphWidth,M.glyphHeight,M.cellWidth,M.cellHeight,M.atlasBlocks),this.log("Default 8x8 font atlas loaded")}await this.waitForReady(),this.log("Initialized and ready")}async waitForReady(t=100,e=50){this.log("Waiting for renderer to be ready");let i=0;return new Promise((r,n)=>{let s=y(()=>{if(i++,this.renderer.isReady())this.log(`Renderer ready after ${i*e}ms`),r();else if(i>=t){let d=`Renderer failed to be ready after ${t*e}ms`;this.log(d),n(new Error(d))}else setTimeout(s,e)},"check");s()})}async loadDefaultFont(t){console.warn("[RendererManager] loadDefaultFont() is deprecated. Use ImageFont instead.")}render(t,e){if(!this.renderer.isReady())return console.warn("[RENDERER MANAGER] Renderer not ready"),!1;let i=t.getRenderState(e);if(!i||i.displays.length===0)return console.warn("[RENDERER MANAGER] No render state or no displays"),!1;let r=i.displays[0];return this.renderer.renderDisplayData(r),!0}getRenderer(){return this.renderer}getCanvas(){return this.renderer.getCanvas()}getAvailableSize(){if(this.renderer.getAvailableSize)return this.renderer.getAvailableSize();let t=this.getCanvas();return{width:t?.clientWidth||t?.width||0,height:t?.clientHeight||t?.height||0}}isReady(){return this.renderer.isReady()}destroy(){this.log("Destroying renderer"),this.renderer.destroy()}log(t){this.options.debug&&console.warn(`[RendererManager] ${t}`)}};y(L,"RendererManager");var B=L;var $=class ${constructor(t){a(this,"core");a(this,"rendererManager");a(this,"rendererType");a(this,"options");a(this,"running",!1);a(this,"startTime",0);a(this,"lastTimestamp",0);a(this,"userId","");a(this,"visibilityChangeHandler");a(this,"tickRate",30);a(this,"accumulatedTime",0);a(this,"FRAME_TIME_MIN",1e3/60);a(this,"lastRenderTimestamp",0);a(this,"rafId",0);a(this,"firstTickDone",!1);a(this,"performanceMonitor");a(this,"renderMode","continuous");a(this,"renderRequested",!1);this.options={application:t.application,container:t.container,debug:t.debug??!1,width:t.width??80,height:t.height??25,renderer:t.renderer??"webgl",useImageDataRendering:t.useImageDataRendering??!0,renderMode:t.renderMode??"continuous"},this.log("Initializing BaseClientRuntime"),this.core=new Z({mode:"client",maxUsers:1}),this.core.onPaletteChanged(i=>{this.onCorePaletteChanged(i)}),this.core.onBitmapFontChanged(i=>{this.onCoreBitmapFontChanged(i)}),this.core.onImageFontChanged(i=>{this.onCoreImageFontChanged(i)}),this.visibilityChangeHandler=()=>{!document.hidden&&this.running&&(this.lastTimestamp=performance.now(),this.accumulatedTime=0,this.log("Tab visible: Reset timing"))},document.addEventListener("visibilitychange",this.visibilityChangeHandler);let e=this.createRenderer();this.rendererType=this.options.renderer,this.rendererManager=new B(e,{debug:this.options.debug,useImageDataRendering:this.options.useImageDataRendering}),this.performanceMonitor=new E(60),this.tickRate=20,this.renderMode=t.renderMode??"continuous",this.log(`Configured: tickRate=${this.tickRate}, renderMode=${this.renderMode}`),this.log("BaseClientRuntime initialized")}createRenderer(){if((this.options.renderer??"webgl")==="terminal2d"){this.log("Creating Terminal 2D renderer");let n=new te(this.options.container,{fixedCols:this.options.width,fixedRows:this.options.height,cellAspectRatio:1});return this.options.useImageDataRendering&&(this.log("Enabling ImageData rendering (pixel-perfect, optimized)"),n.setImageDataRendering(!0)),this.log("Canvas 2D renderer created successfully"),n}this.log("Creating TerminalGL renderer");let i=document.createElement("canvas").getContext("webgl");if(!i)throw new Error("WebGL not supported. TerminalGL requires WebGL 1.0.");if(!i.getExtension("OES_element_index_uint"))throw new Error("OES_element_index_uint extension not supported. TerminalGL requires this extension for large terminals (256x256).");if(!(this.options.container instanceof HTMLDivElement))throw new Error(`TerminalGL requires container to be an HTMLDivElement. Received: ${this.options.container.tagName}`);let r=new ee(this.options.container,{cols:this.options.width,rows:this.options.height,charWidth:8,charHeight:8});return this.log("TerminalGL created successfully"),r}getRendererType(){return this.rendererType}isRunning(){return this.running}setTickRate(t){if(t<0||t>1e3)throw new Error(`Invalid tick rate: ${t}. Must be between 0 and 1000.`);if(this.tickRate=t,this.userId){let e=this.core.getUser(this.userId);e&&e.setBytesTickRate(t>0?t:20)}t===0&&this.renderMode==="continuous"?(this.renderMode="on-demand",this.log("Tick rate set to 0, switched to on-demand render mode")):this.log(`Tick rate set to ${t} TPS`)}setRenderMode(t){this.renderMode=t,this.log(`Render mode set to ${t}`)}setMaxFPS(t){if(t<=0||t>240)throw new Error(`Invalid FPS: ${t}. Must be between 1 and 240.`);this.FRAME_TIME_MIN=1e3/t,this.log(`Max FPS set to ${t}`)}getTickRate(){return this.tickRate}requestRender(){this.renderMode==="on-demand"&&(this.renderRequested=!0,this.log("Render requested"))}async start(){if(this.running){this.log("Already running");return}this.log("Starting BaseClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this),await this.onAfterInit(),await this.createLocalUser(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onBeforeStart(){}async onAfterInit(){}async stop(){if(!this.running)return;this.log("Stopping BaseClientRuntime"),this.running=!1,this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0),await this.onStop();let t=this.core.getUser(this.userId);t&&this.options.application.destroyUser&&this.options.application.destroyUser(this.core,t,"Runtime stopped"),this.log("BaseClientRuntime stopped")}async onStop(){}getStats(){let t=this.performanceMonitor.getStats();return{running:this.running,userCount:this.core.getUsers().length,fps:t.fps,uptime:this.running?performance.now()-this.startTime:0,totalFrames:0,lastFrameTiming:t.lastFrame,avgFrameTiming:t.avgFrame}}getTotalBytesReceived(){return this.core.getUser(this.userId)?.getTotalBytesReceived()??0}getBytesReceivedPerSecond(){return this.core.getUser(this.userId)?.getBytesReceivedPerSecond()??0}resetBytesReceived(){let t=this.core.getUser(this.userId);t&&t.resetByteCounters()}async destroy(){await this.stop(),this.log("Destroying BaseClientRuntime"),this.visibilityChangeHandler&&(document.removeEventListener("visibilitychange",this.visibilityChangeHandler),this.visibilityChangeHandler=void 0),await this.onDestroy();let t=this.rendererManager.getRenderer();t&&"clearOnResizeCallback"in t&&t.clearOnResizeCallback(),this.options.application.destroy&&this.options.application.destroy(),this.rendererManager.destroy(),this.log("BaseClientRuntime destroyed")}async onDestroy(){}async createLocalUser(){this.userId="local",this.log(`Creating local user: ${this.userId}`);let t=this.core.createUser(this.userId,"User");t.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.onUserCreated(t),this.log("Calling application.initUser()"),this.options.application.initUser(this.core,t,{username:"User"}),this.processInitialOrders(t);let e=this.core.generateAllLoadPackets();e.forEach(s=>{t.recordBytesReceived(s.length)});let i=this.core.generateMacroLoadPackets(this.userId);i.forEach(s=>{t.recordBytesReceived(s.length)});let r=t.getInputBindingsLoadPacket();r&&t.recordBytesReceived(r.length),this.log(`Simulated ${e.length} load packets, ${i.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((s,o)=>{let d=this.core.getUser(o);d&&d.recordBytesReceived(s.length)}),this.render(),this.log("Initial render complete")}onUserCreated(t){}processInitialOrders(t){if(t.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let e=t.flushMacroOrders();t.applyMacroOrders(e)}}mainLoop(t){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(t),this.render()),this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}let e=t-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&e<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.lastRenderTimestamp=t;let i=(t-this.lastTimestamp)/1e3,r=Math.min(i,.1);this.lastTimestamp=t,this.performanceMonitor.updateFPS(t);let n=this.core.getUser(this.userId);if(!n){this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}if(this.tickRate===0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(0,l),this.rafId=requestAnimationFrame(p=>this.mainLoop(p));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=t,this.accumulatedTime=0,this.rafId=requestAnimationFrame(c=>this.mainLoop(c));return}this.accumulatedTime+=r;let s=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=s);let o=0,d=0;for(;this.accumulatedTime>=s&&o<5;){let c=performance.now();this.collectAndApplyInput(n),this.options.application.update(this.core,s),this.options.application.updateUser(this.core,n,s),this.processTickOrders(n);let l=n.updateMacros();n.processMacroEvents(l),this.core.endTickSplit().forEach(({static:u,dynamic:h})=>{let g=(u?.length??0)+(h?.length??0);n.recordBytesReceived(g)}),d+=performance.now()-c,this.accumulatedTime-=s,o++}if(o>0){let c=performance.now();this.render();let l=performance.now()-c;this.performanceMonitor.recordFrameTiming(d,l)}this.rafId=requestAnimationFrame(c=>this.mainLoop(c))}collectAndApplyInput(t){}processTickOrders(t){if(t.clearTextInputs(),t.hasPendingMacroOrders()){let e=t.flushMacroOrders();t.applyMacroOrders(e)}}render(){let t=this.core.getUser(this.userId);if(t){let e=t.getMacroRenderOrders();for(let[i,r]of e){let n=t.getLayerById(i);n&&r.length>0&&n.addTemporaryOrders(r)}}this.rendererManager.render(this.core,this.userId)}onCorePaletteChanged(t){this.log(`Palette changed, updating renderer (${t.size} colors)`);let e=[];for(let r=0;r<256;r++){let n=t.get(r);n?e.push({r:n.r,g:n.g,b:n.b,a:n.a}):e.push({r:0,g:0,b:0,a:0})}let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update palette: renderer is null");return}"setPalette"in i&&typeof i.setPalette=="function"&&(i.setPalette(e),this.log("\u2713 Renderer palette updated successfully"))}onCoreBitmapFontChanged(t){this.log(`Bitmap font ${t} changed, updating renderer`);let e=this.core.getBitmapFont(t);if(!e){console.warn(`[BaseClientRuntime] Font ${t} not found in registry`);return}let i=new Map,r=0;for(let s=0;s<256;s++){let o=e.getGlyph(s);o&&(i.set(s,o),r++)}this.log(`Built bitmap font map with ${r} glyphs, dimensions: ${e.getCharWidth()}x${e.getCharHeight()}`);let n=this.rendererManager.getRenderer();if(!n){console.warn("[BaseClientRuntime] Cannot update font: renderer is null");return}"setBitmapFont"in n&&typeof n.setBitmapFont=="function"&&(n.setBitmapFont(i,e.getCharWidth(),e.getCharHeight(),e.getCellWidth(),e.getCellHeight()),this.log("\u2713 Renderer bitmap font updated successfully"),this.onFontChanged(n))}onCoreImageFontChanged(t){this.log(`Image font ${t} changed, updating renderer`);let e=this.core.getImageFont(t);if(!e){console.warn(`[BaseClientRuntime] Image font ${t} not found in registry`);return}this.log(`Loading image font with ${e.getAtlasBlocks()} blocks, glyph: ${e.getGlyphWidth()}x${e.getGlyphHeight()}`);let i=this.rendererManager.getRenderer();if(!i){console.warn("[BaseClientRuntime] Cannot update image font: renderer is null");return}"setImageFont"in i&&typeof i.setImageFont=="function"&&(i.setImageFont(e.getImageData(),e.getGlyphWidth(),e.getGlyphHeight(),e.getCellWidth(),e.getCellHeight(),e.getAtlasBlocks()),this.log("\u2713 Renderer image font updated successfully"),this.onFontChanged(i))}onFontChanged(t){}log(t){this.options.debug&&console.warn(`[BaseClientRuntime] ${t}`)}getCore(){return this.core}getRendererManager(){return this.rendererManager}getUserId(){return this.userId}};y($,"BaseClientRuntime");var w=$;import{Core as re,PostProcessOrderType as R,PostProcessOrderCollector as ne}from"@utsp/core";import{AutoplayOverlay as se,PostProcessOverlay as J}from"@utsp/render";import{ScalingMode as oe,valueToScalingMode as ae,Vector2 as de}from"@utsp/types";import{UnifiedInputRouter as le,InputCollector as P,MobileVibration as ce}from"@utsp/input";import{SocketIOClient as he}from"@utsp/network-client";import{AudioManager as ue}from"@utsp/audio";import{encodeCompressedInput as ie}from"@utsp/core";import{InputCollector as k}from"@utsp/input";var V=class V{constructor(t,e,i,r,n){a(this,"network");a(this,"core");a(this,"rendererManager");a(this,"performanceMonitor");a(this,"options");a(this,"audioManager",null);a(this,"postProcessCallback",null);a(this,"userId","");a(this,"lastReceivedTick",-1);a(this,"connected",!1);a(this,"totalBytesReceived",0);a(this,"lastSentViewports",new Map);this.network=t,this.core=e,this.rendererManager=i,this.performanceMonitor=r,this.options={serverUrl:n.serverUrl,username:n.username,token:n.token??"",debug:n.debug??!1,autoReconnect:n.autoReconnect??!0,canvasWidth:n.canvasWidth,canvasHeight:n.canvasHeight}}async connect(){this.log(`Connecting to ${this.options.serverUrl}`),await this.network.connect(),this.connected=!0,this.log("Connected to server"),this.setupNetworkHandlers()}async joinGame(t){return new Promise((e,i)=>{this.network.send("join",{username:this.options.username,token:this.options.token}),this.network.on("join_response",r=>{if(r.success){this.userId=r.userId,this.log(`Joined game as ${this.userId}`);let n=this.core.createUser(this.userId,this.options.username);n.setBytesTickRate(20),this.audioManager&&n.setAudioProcessor(this.audioManager),t.initUser(this.core,n,{username:this.options.username,token:this.options.token}),e(this.userId)}else i(new Error(r.error||"Failed to join game"))}),setTimeout(()=>i(new Error("Join timeout")),5e3)})}sendInput(t){let e=this.core.getUser(this.userId);if(!e)return;let i=this.rendererManager.getCanvas();if(i){let v=this.rendererManager.getRenderer()?.getOffsets?.(),S={offsetX:v?.offsetX??0,offsetY:v?.offsetY??0},T=k.collectMousePosition(t,i,this.options.canvasWidth,this.options.canvasHeight,S);e.setMousePosition(T.x,T.y,T.over),e.updateMacroMouse(T.x,T.y,T.isLeftDown),k.collectTouchPositions(t,i,this.options.canvasWidth,this.options.canvasHeight,10,S).forEach(U=>{e.setTouchPosition(U.id,U.x,U.y,U.over)})}let r=e.getInputBindingRegistry(),n=r.getAllAxes(),s=r.getAllButtons(),o=k.collectAxisSources(n,t),d=k.collectButtonSources(s,t),c=k.collectTextInputs(t);c.length>0&&e.setTextInputs(c);let l=n.sort((f,v)=>f.bindingId-v.bindingId),p=new Map;for(let f of l){let v=r.evaluateAxis(f.bindingId,o);p.set(f.bindingId,v),e.setAxis(f.name,v)}let u=s.sort((f,v)=>f.bindingId-v.bindingId),h=new Map;for(let f of u){let v=r.evaluateButton(f.bindingId,d);h.set(f.bindingId,v),e.setButton(f.name,v)}let g=e.getMouseDisplayInfo(),m=e.getIsMouseOnADisplay(),b=this.collectChangedViewports(),C=ie(0n,p,h,g?.displayId??0,g?.localX??0,g?.localY??0,m,c,[],b);this.network.send("input",C)}collectChangedViewports(){let t=[],{width:e,height:i}=this.rendererManager.getAvailableSize();if(e===0||i===0)return t;let r=0,n=this.lastSentViewports.get(r);return(!n||n.pixelWidth!==e||n.pixelHeight!==i)&&(t.push({displayId:r,pixelWidth:e,pixelHeight:i}),this.lastSentViewports.set(r,{pixelWidth:e,pixelHeight:i}),this.log(`Viewport changed: display ${r} = ${e}x${i}px`)),t}disconnect(){this.connected&&(this.network.send("leave",{}),this.network.disconnect(),this.connected=!1,this.log("Disconnected from server"))}getUserId(){return this.userId}isConnected(){return this.connected}getTotalBytesReceived(){return this.totalBytesReceived}destroy(){this.disconnect(),this.network.destroy(),this.log("NetworkSync destroyed")}setupNetworkHandlers(){this.network.on("update",t=>{try{let e=this.convertToUint8Array(t,"update");if(!e)return;this.recordBytesReceived(e.length);let i=this.core.applyUpdatePacketBuffer(this.userId,e);i?(this.postProcessCallback&&i.postProcessOrders&&i.postProcessOrders.length>0&&this.postProcessCallback(i.postProcessOrders),this.rendererManager.render(this.core,this.userId)):this.log("Failed to apply update packet")}catch(e){this.log(`Error applying update packet: ${e}`)}}),this.network.on("update-static",t=>{this.handleUpdatePacket(t,"update-static")}),this.network.on("update-dynamic",t=>{this.handleUpdatePacket(t,"update-dynamic")}),this.network.on("load",t=>{try{let e=this.convertToUint8Array(t,"load");if(!e)return;this.recordBytesReceived(e.length),this.core.applyLoadPacket(e)?this.log("Load packet applied successfully"):this.log("Failed to apply load packet")}catch(e){this.log(`Error applying load packet: ${e}`)}}),this.network.on("input-bindings",t=>{this.recordBytesReceived(t.length),this.core.applyInputBindingsLoadPacket(this.userId,t)?this.log("Input bindings configured"):this.log("Failed to apply input bindings")}),this.audioManager&&this.setupSoundHandlers(),this.network.on("disconnect",()=>{this.connected=!1,this.log("Disconnected from server")})}handleUpdatePacket(t,e){try{let i=this.convertToUint8Array(t,e);if(!i)return;this.recordBytesReceived(i.length);let r=new DataView(i.buffer,i.byteOffset,i.byteLength),n=Number(r.getBigUint64(0,!1));if(e==="update-dynamic"&&n<this.lastReceivedTick)return;n>this.lastReceivedTick&&(this.lastReceivedTick=n);let s=this.core.applyUpdatePacketBuffer(this.userId,i);if(s){console.warn(`[CLIENT] ${e}: ${i.length}B (tick ${n}) - APPLIED`),this.postProcessCallback&&s.postProcessOrders&&s.postProcessOrders.length>0&&this.postProcessCallback(s.postProcessOrders),this.options.debug&&this.debugRenderState();let o=performance.now();this.rendererManager.render(this.core,this.userId);let d=performance.now()-o;this.performanceMonitor.recordFrameTiming(0,d),this.options.debug&&console.warn(`[CLIENT] render() completed for ${e} (${d.toFixed(2)}ms)`)}else console.warn(`[CLIENT] Failed to apply ${e} packet (tick ${n})`)}catch(i){this.log(`Error applying ${e} update packet: ${i}`),console.error(i)}}convertToUint8Array(t,e){return t instanceof Uint8Array?t:t instanceof ArrayBuffer?new Uint8Array(t):Array.isArray(t)?new Uint8Array(t):t.data&&Array.isArray(t.data)?new Uint8Array(t.data):typeof t=="object"&&t.type==="Buffer"?new Uint8Array(t.data):(this.log(`Unknown data type for ${e} packet: ${typeof t}`),null)}debugRenderState(){let t=this.core.getUser(this.userId);if(t){let i=t.getLayers();console.warn(`[CLIENT] User has ${i.length} layers`),i.forEach((r,n)=>{let s=r.getOrders(),o=r.getStatic();(s.length>0||o)&&console.warn(` Layer ${n}: ${s.length} orders, static=${o}, z=${r.getZOrder()}`)})}let e=this.rendererManager.isReady();console.warn(`[CLIENT] Renderer ready: ${e}`)}log(t){this.options.debug&&console.warn(`[NetworkSync] ${t}`)}recordBytesReceived(t){this.totalBytesReceived+=t;let e=this.core.getUser(this.userId);e&&e.recordBytesReceived(t)}getSoundPacketSize(t){return t.mode==="file"?t.sounds.reduce((i,r)=>i+r.data.length+r.name.length+10,0):t.sounds.reduce((i,r)=>i+r.url.length+r.name.length+10,0)}setAudioManager(t){if(this.audioManager=t,t&&this.userId){let e=this.core.getUser(this.userId);e&&e.setAudioProcessor(t)}this.connected&&t&&this.setupSoundHandlers()}setPostProcessCallback(t){this.postProcessCallback=t}setupSoundHandlers(){this.audioManager&&(this.network.on("sound-load",async t=>{this.recordBytesReceived(this.getSoundPacketSize(t)),t.mode==="file"?await this.handleSoundLoad(t):t.mode==="external"&&await this.handleSoundExternalLoad(t)}),this.log("Sound handlers registered"))}async handleSoundLoad(t){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromData(i.soundId,i.name,i.data),this.log(`Loaded sound: ${i.name} (${i.data.length} bytes)`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}async handleSoundExternalLoad(t){if(!this.audioManager){this.log("Cannot load external sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load external sounds: SoundBank not initialized");return}for(let i of t.sounds)try{await e.loadFromUrl(i.soundId,i.name,i.url),this.log(`Loaded external sound: ${i.name} from ${i.url}`),this.sendAudioAck({type:"sound-loaded",soundId:i.soundId,name:i.name})}catch(r){console.error(`[NetworkSync] Failed to load external sound "${i.name}":`,r),this.sendAudioAck({type:"sound-error",soundId:i.soundId,name:i.name,error:String(r)})}}sendAudioAck(t){this.network.send("audio-ack",t),this.log(`Sent audio-ack: ${t.type}`)}sendBridge(t,e){this.network.sendBridge(t,e),this.log(`Bridge sent on channel '${t}'`)}onBridge(t,e){this.network.onBridge(t,e),this.log(`Bridge handler registered for channel '${t}'`)}offBridge(t,e){this.network.offBridge(t,e),this.log(`Bridge handler removed for channel '${t}'`)}removeAllBridgeHandlers(t){this.network.removeAllBridgeHandlers(t),this.log(t?`All bridge handlers removed for '${t}'`:"All bridge handlers removed")}};y(V,"NetworkSync");var x=V;var G=class G extends w{constructor(e){super({application:e.application,container:e.container,debug:e.debug,width:e.width,height:e.height,renderer:e.renderer,useImageDataRendering:e.useImageDataRendering,renderMode:e.renderMode});a(this,"input",null);a(this,"networkSync",null);a(this,"mode");a(this,"localOptions",null);a(this,"autoplayOverlay",null);a(this,"autoplay",!0);a(this,"audioManager",null);a(this,"mobileVibration",null);a(this,"postProcessOverlay",null);a(this,"postProcessOrderCollector",new ne);a(this,"localBridgeHandlers",new Map);a(this,"localBridgeWildcardHandlers",new Set);if(this.mode=e.mode,e.mode==="local"?(this.localOptions={mode:"local",application:e.application,container:e.container,debug:e.debug??!1,width:e.width??80,height:e.height??25,userId:e.userId??"local",username:e.username??"User",renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.userId=e.userId??"local",this.autoplay=e.autoplay??!0):(this.localOptions={mode:"connected",application:e.application,container:e.container,serverUrl:e.serverUrl,username:e.username??"Player",debug:e.debug??!1,width:e.width??80,height:e.height??25,autoReconnect:e.autoReconnect??!0,token:e.token,renderer:e.renderer??"webgl",useImageDataRendering:e.useImageDataRendering??!0,mobileInputConfig:e.mobileInputConfig,captureInput:e.captureInput??!1,inputEnabled:e.inputEnabled??!0,autoplay:e.autoplay??!0,autoplayOptions:e.autoplayOptions,renderMode:e.renderMode??"continuous"},this.autoplay=e.autoplay??!0),this.log(`Initializing ClientRuntime (${this.mode} mode)`),this.mode==="connected"&&(this.core=new re({mode:"client",maxUsers:100}),this.core.onPaletteChanged(r=>{this.onCorePaletteChanged(r)}),this.core.onBitmapFontChanged(r=>{this.onCoreBitmapFontChanged(r)}),this.core.onImageFontChanged(r=>{this.onCoreImageFontChanged(r)})),e.inputEnabled??!0?this.input=new le({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:e.debug,keyboardConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mouseConfig:{preventDefault:e.captureInput??!1,stopPropagation:e.captureInput??!1},mobileConfig:{preventDefault:e.mobileInputConfig?.preventDefault??!0,passive:e.mobileInputConfig?.passive??!1,maxTouches:e.mobileInputConfig?.maxTouches??10}}):this.log("Input disabled (inputEnabled: false)"),this.mode==="connected"){let r=this.localOptions,n=new he({url:r.serverUrl,autoReconnect:r.autoReconnect,auth:{username:r.username,token:r.token},debug:e.debug});this.networkSync=new x(n,this.core,this.rendererManager,this.performanceMonitor,{serverUrl:r.serverUrl,username:r.username,token:r.token,debug:e.debug,autoReconnect:r.autoReconnect,canvasWidth:e.width??80,canvasHeight:e.height??25})}this.log("ClientRuntime initialized")}getMode(){return this.mode}setTickRate(e){if(this.mode==="connected"){this.log("setTickRate() has no effect in connected mode");return}super.setTickRate(e)}async onBeforeStart(){this.audioManager=new ue({debug:this.options.debug}),this.mobileVibration=new ce({debug:this.options.debug}),this.autoplay?(this.log("Autoplay enabled, initializing AudioManager..."),this.audioManager.initialize()):(this.log("Autoplay disabled, showing overlay..."),await new Promise(e=>{this.autoplayOverlay=new se(this.options.container,{...this.localOptions?.autoplayOptions,onStart:()=>{this.log("User clicked start button"),this.audioManager&&(this.audioManager.initialize(),this.audioManager.playStartSound()),e()}})}),this.log("Autoplay overlay dismissed, continuing startup..."))}async onAfterInit(){this.postProcessOverlay=new J(this.options.container),this.log("PostProcessOverlay created");let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync&&(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(i=>{this.applyPostProcessOrders(i)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application))}async start(){if(this.running){this.log("Already running");return}this.log("Starting ClientRuntime"),await this.onBeforeStart(),await this.rendererManager.initialize(this.core),this.log("Renderer is ready"),this.postProcessOverlay=new J(this.options.container),this.log("PostProcessOverlay created"),this.onCorePaletteChanged(this.core.getPalette()),this.log("Initial palette sent to renderer"),this.log("Calling application.init()"),await this.options.application.init(this.core,this);let e=this.rendererManager.getCanvas();e&&this.input&&this.input.setMobileTarget(e),this.mode==="connected"&&this.networkSync?(this.log("Connecting to server"),this.audioManager&&this.networkSync.setAudioManager(this.audioManager),this.networkSync.setPostProcessCallback(r=>{this.applyPostProcessOrders(r)}),await this.networkSync.connect(),this.userId=await this.networkSync.joinGame(this.options.application)):await this.createLocalUser();let i=this.core.getUser(this.userId);if(i){let r=i.getDisplays();if(r.length>0){let s=r[0].getSize(),o=s.x,d=s.y,c=this.rendererManager.getRenderer(),l,p;if(this.rendererType==="webgl"){let h=c.getGridSize();l=h.cols,p=h.rows}else{let u=c;l=u.getCols(),p=u.getRows()}(l!==o||p!==d)&&(this.log(`Adjusting renderer from ${l}\xD7${p} to match display ${o}\xD7${d}`),c.resize(o,d))}}this.input&&this.input.start(),this.running=!0,this.startTime=performance.now(),this.lastTimestamp=this.startTime,this.lastRenderTimestamp=this.startTime,this.accumulatedTime=0,this.firstTickDone=!1,this.performanceMonitor.reset(),this.performanceMonitor.startTracking(this.startTime),this.log("Starting main loop"),this.mainLoop(this.lastTimestamp)}async onStop(){this.audioManager&&this.audioManager.stopAll(),this.input&&this.input.stop(),this.mode==="connected"&&this.networkSync&&this.networkSync.disconnect()}getStats(){let e=super.getStats(),r=this.core.getUser(this.userId)?.getTotalBytesReceived();return{...e,mode:this.mode,latency:void 0,totalBytesReceived:r}}async onDestroy(){this.autoplayOverlay&&(this.autoplayOverlay.destroy(),this.autoplayOverlay=null),this.audioManager&&(this.audioManager.destroy(),this.audioManager=null),this.postProcessOverlay&&(this.postProcessOverlay.destroy(),this.postProcessOverlay=null),this.input&&this.input.destroy(),this.networkSync&&this.networkSync.destroy()}async createLocalUser(){let e=this.localOptions;this.userId=e?.userId??"local",this.log(`Creating local user: ${this.userId}`);let i=this.core.createUser(this.userId,e?.username??"User");if(i.setBytesTickRate(this.tickRate>0?this.tickRate:20),this.audioManager&&i.setAudioProcessor(this.audioManager),this.mobileVibration&&i.setMobileVibrationProcessor(this.mobileVibration),this.input){let d=this.input.getGamepad();d&&i.setGamepadVibrationProcessor(d)}if(this.log("Calling application.initUser()"),this.options.application.initUser(this.core,i,{username:e?.username??"User"}),i.hasAudioConfigCommands()&&(this.log("Applying audio config commands from initUser()"),i.applyAudioConfigCommands(i.flushAudioConfigCommands())),i.hasPendingMacroOrders()){this.log("Applying macro orders from initUser()");let d=i.flushMacroOrders();i.applyMacroOrders(d)}if(i.hasPostProcessCommands()){this.log("Applying post-process commands from initUser()");let d=i.flushPostProcessCommands(),c=this.postProcessOrderCollector.convertCommands(d);this.applyPostProcessOrders(c)}i.needsSendSounds()&&(await this.loadSoundsFromRegistry(),i.clearSendSounds());let r=this.core.generateAllLoadPackets();r.forEach(d=>{i.recordBytesReceived(d.length)});let n=this.core.generateMacroLoadPackets(this.userId);n.forEach(d=>{i.recordBytesReceived(d.length)});let s=i.getInputBindingsLoadPacket();s&&i.recordBytesReceived(s.length),this.log(`Simulated ${r.length} load packets, ${n.length} macro packets for byte counting`),this.log("Performing initial render"),this.core.endTick().forEach((d,c)=>{let l=this.core.getUser(c);l&&l.recordBytesReceived(d.length)}),this.render(),this.log("Initial render complete")}async loadSoundsFromRegistry(){if(!this.audioManager){this.log("Cannot load sounds: AudioManager not initialized");return}let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=this.core.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry into AudioManager...`);let n=this.core.getUser(this.userId),s=0;for(let o of r)try{if(o.loadType==="file"&&o.data){await e.loadFromData(o.soundId,o.name,o.data);let d=o.data.length+o.name.length+10;s+=d,this.log(`\u{1F50A} Loaded sound: ${o.name} (${o.data.length} bytes)`)}else if(o.loadType==="external"&&o.url){await e.loadFromUrl(o.soundId,o.name,o.url);let d=o.url.length+o.name.length+10;s+=d,this.log(`\u{1F50A} Loaded external sound: ${o.name} from ${o.url}`)}}catch(d){console.error(`[ClientRuntime] Failed to load sound "${o.name}":`,d)}n&&s>0&&(n.recordBytesReceived(s),this.log(`Simulated ${s} bytes for ${r.length} sound(s)`)),this.log(`\u2713 ${e.size} sounds loaded into AudioManager`)}mainLoop(e){if(!this.running){this.rafId&&(cancelAnimationFrame(this.rafId),this.rafId=0);return}if(this.renderMode==="on-demand"){this.renderRequested&&(this.renderRequested=!1,this.performanceMonitor.updateFPS(e),this.render()),this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}let i=e-this.lastRenderTimestamp;if(this.lastRenderTimestamp>0&&i<this.FRAME_TIME_MIN-1){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}this.lastRenderTimestamp=e;let r=(e-this.lastTimestamp)/1e3,n=Math.min(r,.1);this.lastTimestamp=e,this.performanceMonitor.updateFPS(e);let s=this.core.getUser(this.userId);if(!s){this.rafId=requestAnimationFrame(o=>this.mainLoop(o));return}if(this.mode==="local"){if(this.tickRate===0){let l=performance.now();this.render();let p=performance.now()-l;this.performanceMonitor.recordFrameTiming(0,p),this.rafId=requestAnimationFrame(u=>this.mainLoop(u));return}if(!this.firstTickDone){this.firstTickDone=!0,this.lastTimestamp=e,this.accumulatedTime=0,this.rafId=requestAnimationFrame(l=>this.mainLoop(l));return}this.accumulatedTime+=n;let o=1/this.tickRate;this.accumulatedTime>.5&&(this.accumulatedTime=o);let d=0,c=0;for(;this.accumulatedTime>=o&&d<5;){let l=performance.now();if(this.collectAndApplyInput(s),this.options.application.update(this.core,o),this.options.application.updateUser(this.core,s,o),s.clearTextInputs(),s.hasAudioConfigCommands()&&s.applyAudioConfigCommands(s.flushAudioConfigCommands()),s.hasSoundCommands()&&s.applyAudioCommands(s.flushSoundCommands()),s.hasMobileVibrationCommands()&&s.applyMobileVibrationCommands(s.flushMobileVibrationCommands()),s.hasGamepadVibrationCommands()&&s.applyGamepadVibrationCommands(s.flushGamepadVibrationCommands()),s.hasPostProcessCommands()){let h=s.flushPostProcessCommands(),g=this.postProcessOrderCollector.convertCommands(h);this.applyPostProcessOrders(g)}if(s.hasPendingMacroOrders()){let h=s.flushMacroOrders();s.applyMacroOrders(h)}let p=s.updateMacros();if(s.processMacroEvents(p),s.hasBridgeMessages()){let h=s.getBridgeMessages();this.dispatchLocalBridgeMessages(h)}this.core.endTickSplit().forEach(({static:h,dynamic:g})=>{let m=(h?.length??0)+(g?.length??0);s.recordBytesReceived(m)}),c+=performance.now()-l,this.accumulatedTime-=o,d++}if(d>0){let l=performance.now();this.render();let p=performance.now()-l;this.performanceMonitor.recordFrameTiming(c,p)}}else{let o=s.updateMacros();s.processMacroEvents(o),this.collectAndSendInput()}this.rafId=requestAnimationFrame(o=>this.mainLoop(o))}collectAndApplyInput(e){if(!this.input)return;let i=this.rendererManager.getCanvas();if(i){let d=e.getDisplays(),c=d.length>0?d[0].getSize():{x:this.options.width,y:this.options.height},l=c.x,p=c.y,h=this.rendererManager.getRenderer()?.getOffsets?.(),g={offsetX:h?.offsetX??0,offsetY:h?.offsetY??0},m=P.collectMousePosition(this.input,i,l,p,g);e.setMousePosition(m.x,m.y,m.over),e.updateMacroMouse(m.x,m.y,m.isLeftDown),P.collectTouchPositions(this.input,i,l,p,10,g).forEach(f=>{e.setTouchPosition(f.id,f.x,f.y,f.over)});let C=this.rendererManager.getAvailableSize();e.setDisplayViewport(0,C.width,C.height)}let r=P.collectTextInputs(this.input);r.length>0&&e.setTextInputs(r);let n=e.getInputBindingRegistry(),s=n.getAllAxes(),o=n.getAllButtons();if(s.length>0||o.length>0){let d=P.collectAxisSources(s,this.input),c=P.collectButtonSourcesWithTransitions(o,this.input);s.forEach(l=>{let p=n.evaluateAxis(l.bindingId,d);e.setAxis(l.name,p)}),o.forEach(l=>{let p=new Map,u=new Map,h=new Map;for(let[C,f]of c)p.set(C,f.pressed),u.set(C,f.justPressed),h.set(C,f.justReleased);let g=n.evaluateButton(l.bindingId,p),m=n.evaluateButton(l.bindingId,u),b=n.evaluateButton(l.bindingId,h);e.setButton(l.name,g),e.setButton(`${l.name}_justPressed`,m),e.setButton(`${l.name}_justReleased`,b)})}this.input.poll?.()}collectAndSendInput(){!this.networkSync||!this.input||this.networkSync.sendInput(this.input)}render(){let e=this.core.getUser(this.userId);if(e){let i=e.getDisplays();if(i.length>0){let r=i[0],n=r.getSize(),s=this.rendererManager.getRenderer();if(s){let o,d,c,l,p=null;if(this.rendererType==="webgl"){let h=s,g=h.getGridSize();o=g.cols,d=g.rows,c=h.getCellWidth(),l=h.getCellHeight(),p=h.getScalingMode()}else{let h=s;o=h.getCols(),d=h.getRows(),c=h.getCellWidth(),l=h.getCellHeight(),p=h.getScalingMode()}if(p===oe.Responsive){let h=this.rendererManager.getAvailableSize(),g=256*256;"getMaxCells"in s&&(g=Math.min(g,s.getMaxCells()));let m=Math.floor(h.width/c),b=Math.floor(h.height/l);if(m=Math.min(256,Math.max(1,m)),b=Math.min(256,Math.max(1,b)),m*b>g){let C=Math.sqrt(g/(m*b));m=Math.max(1,Math.floor(m*C)),b=Math.max(1,Math.floor(b*C)),this.log(`\u26A0\uFE0F Device limit: max ${g} cells, clamping to ${m}\xD7${b}`)}(n.x!==m||n.y!==b)&&(this.log(`\u{1F4D0} Resizing display from ${n.x}\xD7${n.y} to ${m}\xD7${b} (available: ${h.width}\xD7${h.height}px, cell: ${c}\xD7${l}px)`),r.setSize(new de(m,b)))}let u=r.getSize();(o!==u.x||d!==u.y)&&(this.log(`\u{1F4D0} Resizing renderer from ${o}\xD7${d} to ${u.x}\xD7${u.y}`),s.resize(u.x,u.y))}}}super.render()}onFontChanged(e){this.postProcessOverlay&&"syncWithRenderer"in this.postProcessOverlay&&"getCellWidth"in e&&"getCellHeight"in e&&"getCurrentScale"in e&&"getGridSize"in e&&(this.postProcessOverlay.syncWithRenderer(e),this.log("\u2713 PostProcessOverlay synced with renderer dimensions"))}getAudioManager(){return this.audioManager}getAudioContext(){return this.audioManager?.getContext()??null}getPostProcessOverlay(){return this.postProcessOverlay}applyAmbientEffectConfig(e){let i=this.rendererManager?.getRenderer();i&&"setAmbientEffect"in i&&(e.enabled?i.setAmbientEffect({blur:e.blur,scale:e.scale}):i.setAmbientEffect(!1),this.log(`Ambient effect ${e.enabled?"enabled":"disabled"}`))}applyPostProcessOrders(e){if(!this.postProcessOverlay){this.log("PostProcessOverlay not initialized, skipping post-process orders");return}for(let i of e){let r=i.displayId;switch(i.type){case R.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.postProcessOverlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffectConfig(n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case R.SetScanlines:{this.postProcessOverlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case R.SetAmbientEffect:{this.applyAmbientEffectConfig({enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case R.SetScalingMode:{this.applyScalingMode(r,i.mode);break}case R.SetGrid:{this.applyGridConfig(r,i);break}case R.SwitchPalette:{this.applySwitchPalette(r,i);break}case R.SetCellSize:{this.applyCellSize(r,i.cellWidth,i.cellHeight);break}}}}applyScalingMode(e,i){let r=this.rendererManager.getRenderer();if(r&&"setScalingMode"in r){let n=ae(i);r.setScalingMode(n),this.log(`[Display ${e}] Scaling mode set to ${n}`)}}applyGridConfig(e,i){let r=this.rendererManager.getRenderer();if(r&&"setGrid"in r){let n=i.colorA/255;r.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${n})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}applySwitchPalette(e,i){let r=this.core.getPaletteFromSlot(i.slotId);if(!r){console.warn(`[ClientRuntime] Palette slot ${i.slotId} not found`);return}let n=[];for(let o=0;o<256;o++){let d=r.get(o);d?n.push({r:d.r,g:d.g,b:d.b,a:d.a}):n.push({r:0,g:0,b:0,a:0})}let s=this.rendererManager.getRenderer();s&&"setPalette"in s&&(s.setPalette(n),this.log(`[Display ${e}] Switched to palette slot ${i.slotId}`))}applyCellSize(e,i,r){let n=this.rendererManager.getRenderer();n&&"setCellSize"in n&&(n.setCellSize(i,r),this.log(`[Display ${e}] Cell size set to ${i}\xD7${r}px`))}patternFromType(e){switch(e){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}dispatchLocalBridgeMessages(e){for(let i of e){let{channel:r,data:n}=i,s=this.localBridgeHandlers.get(r);if(s)for(let o of s)try{o(n)}catch(d){console.error(`[ClientRuntime] Bridge handler error on '${r}':`,d)}for(let o of this.localBridgeWildcardHandlers)try{o(r,n)}catch(d){console.error("[ClientRuntime] Bridge wildcard handler error:",d)}this.log(`Bridge message dispatched on channel '${r}'`)}}sendBridge(e,i){if(this.mode==="connected"&&this.networkSync)this.networkSync.sendBridge(e,i);else if(this.mode==="local"){let r=this.core.getUser(this.userId);r&&this.options.application.onBridgeMessage&&(this.options.application.onBridgeMessage(this.core,r,e,i),this.log(`Bridge message sent to application on channel '${e}'`))}}onBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.onBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.add(i):(this.localBridgeHandlers.has(e)||this.localBridgeHandlers.set(e,new Set),this.localBridgeHandlers.get(e).add(i)),this.log(`Bridge handler registered for channel '${e}'`))}offBridge(e,i){this.mode==="connected"&&this.networkSync?this.networkSync.offBridge(e,i):this.mode==="local"&&(e==="*"?this.localBridgeWildcardHandlers.delete(i):this.localBridgeHandlers.get(e)?.delete(i))}removeAllBridgeHandlers(e){this.mode==="connected"&&this.networkSync?this.networkSync.removeAllBridgeHandlers(e):this.mode==="local"&&(e===void 0?(this.localBridgeHandlers.clear(),this.localBridgeWildcardHandlers.clear()):e==="*"?this.localBridgeWildcardHandlers.clear():this.localBridgeHandlers.delete(e))}log(e){this.options.debug&&console.warn(`[ClientRuntime/${this.mode}] ${e}`)}};y(G,"ClientRuntime");var D=G;import{UnifiedInputRouter as pe,InputCollector as A,MobileVibration as ge}from"@utsp/input";var W=class W{constructor(t={}){a(this,"input");a(this,"mobileVibration");this.input=new pe({enableKeyboardMouse:!0,enableGamepad:!0,enableMobile:!0,targetElement:window,mobileTargetElement:void 0,debug:t.debug,keyboardConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mouseConfig:{preventDefault:t.captureInput??!1,stopPropagation:t.captureInput??!1},mobileConfig:{preventDefault:t.mobileInputConfig?.preventDefault??!0,passive:t.mobileInputConfig?.passive??!1,maxTouches:t.mobileInputConfig?.maxTouches??10}}),this.mobileVibration=new ge({debug:t.debug})}setMobileTarget(t){this.input.setMobileTarget(t)}start(){this.input.start()}stop(){this.input.stop()}destroy(){this.input.destroy()}getRouter(){return this.input}getGamepad(){return this.input.getGamepad()}getMobileVibration(){return this.mobileVibration}collectAndApply(t,e,i,r,n){if(e){let l=A.collectMousePosition(this.input,e,i,r,n);t.setMousePosition(l.x,l.y,l.over),t.updateMacroMouse(l.x,l.y,l.isLeftDown),A.collectTouchPositions(this.input,e,i,r,10,n).forEach(u=>{t.setTouchPosition(u.id,u.x,u.y,u.over)})}let s=A.collectTextInputs(this.input);s.length>0&&t.setTextInputs(s);let o=t.getInputBindingRegistry(),d=o.getAllAxes(),c=o.getAllButtons();if(d.length>0||c.length>0){let l=A.collectAxisSources(d,this.input),p=A.collectButtonSourcesWithTransitions(c,this.input);d.forEach(u=>{let h=o.evaluateAxis(u.bindingId,l);t.setAxis(u.name,h)}),c.forEach(u=>{let h=new Map,g=new Map,m=new Map;for(let[v,S]of p)h.set(v,S.pressed),g.set(v,S.justPressed),m.set(v,S.justReleased);let b=o.evaluateButton(u.bindingId,h),C=o.evaluateButton(u.bindingId,g),f=o.evaluateButton(u.bindingId,m);t.setButton(u.name,b),t.setButton(`${u.name}_justPressed`,C),t.setButton(`${u.name}_justReleased`,f)})}this.input.poll?.()}processVibrationCommands(t){t.hasMobileVibrationCommands()&&t.applyMobileVibrationCommands(t.flushMobileVibrationCommands()),t.hasGamepadVibrationCommands()&&t.applyGamepadVibrationCommands(t.flushGamepadVibrationCommands())}};y(W,"InputFeature");var q=W;import{AudioManager as me}from"@utsp/audio";var H=class H{constructor(t={}){a(this,"audioManager");a(this,"debug");this.debug=t.debug??!1,this.audioManager=new me({debug:t.debug})}initialize(){this.audioManager.initialize()}playStartSound(){this.audioManager.playStartSound()}stopAll(){this.audioManager.stopAll()}destroy(){this.audioManager.destroy()}getManager(){return this.audioManager}getContext(){return this.audioManager.getContext()}injectIntoUser(t){t.setAudioProcessor(this.audioManager)}processAudioCommands(t){t.hasAudioConfigCommands()&&t.applyAudioConfigCommands(t.flushAudioConfigCommands()),t.hasSoundCommands()&&t.applyAudioCommands(t.flushSoundCommands())}async loadSoundsFromRegistry(t){let e=this.audioManager.getSoundBank();if(!e){this.log("Cannot load sounds: SoundBank not initialized");return}let r=t.getSoundRegistry().getAll();if(r.length===0){this.log("No sounds to load from registry");return}this.log(`Loading ${r.length} sounds from Core registry...`);for(let n of r)try{n.loadType==="file"&&n.data?(await e.loadFromData(n.soundId,n.name,n.data),this.log(`Loaded sound: ${n.name} (${n.data.length} bytes)`)):n.loadType==="external"&&n.url&&(await e.loadFromUrl(n.soundId,n.name,n.url),this.log(`Loaded external sound: ${n.name} from ${n.url}`))}catch(s){console.error(`[AudioFeature] Failed to load sound "${n.name}":`,s)}this.log(`${e.size} sounds loaded`)}log(t){this.debug&&console.warn(`[AudioFeature] ${t}`)}};y(H,"AudioFeature");var Q=H;import{PostProcessOrderCollector as fe,PostProcessOrderType as F}from"@utsp/core";import{PostProcessOverlay as ye}from"@utsp/render";import{valueToScalingMode as be}from"@utsp/types";var z=class z{constructor(t,e={}){a(this,"overlay");a(this,"orderCollector");a(this,"debug");this.debug=e.debug??!1,this.overlay=new ye(t),this.orderCollector=new fe}destroy(){this.overlay.destroy()}getOverlay(){return this.overlay}syncWithRenderer(t){this.overlay.syncWithRenderer(t),this.log("Overlay synced with renderer dimensions")}processCommands(t,e){if(!t.hasPostProcessCommands())return;let i=t.flushPostProcessCommands(),r=this.orderCollector.convertCommands(i);this.applyOrders(r,e)}applyOrders(t,e){for(let i of t){let r=i.displayId;switch(i.type){case F.SetConfig:{let n={};i.scanlines&&(n.scanlines={enabled:i.scanlines.enabled,opacity:i.scanlines.opacity,pattern:this.patternFromType(i.scanlines.pattern),color:{r:i.scanlines.colorR,g:i.scanlines.colorG,b:i.scanlines.colorB}}),i.ambientEffect&&(n.ambientEffect={enabled:i.ambientEffect.enabled,blur:i.ambientEffect.blur,scale:i.ambientEffect.scale}),this.overlay.setConfig(Object.keys(n).length>0?n:null),n.ambientEffect&&this.applyAmbientEffect(e,n.ambientEffect),this.log(`[Display ${r}] SetConfig applied`);break}case F.SetScanlines:{this.overlay.setScanlines({enabled:i.enabled,opacity:i.opacity,pattern:this.patternFromType(i.pattern),color:{r:i.colorR,g:i.colorG,b:i.colorB}}),this.log(`[Display ${r}] Scanlines ${i.enabled?"enabled":"disabled"}`);break}case F.SetAmbientEffect:{this.applyAmbientEffect(e,{enabled:i.enabled,blur:i.blur,scale:i.scale}),this.log(`[Display ${r}] Ambient effect ${i.enabled?"enabled":"disabled"}`);break}case F.SetScalingMode:{this.applyScalingMode(e,r,i.mode);break}case F.SetGrid:{this.applyGridConfig(e,r,i);break}}}}applyAmbientEffect(t,e){t&&"setAmbientEffect"in t&&(e.enabled?t.setAmbientEffect({blur:e.blur,scale:e.scale}):t.setAmbientEffect(!1))}applyScalingMode(t,e,i){if(t&&"setScalingMode"in t){let r=be(i);t.setScalingMode(r),this.log(`[Display ${e}] Scaling mode set to ${r}`)}}applyGridConfig(t,e,i){if(t&&"setGrid"in t){let r=i.colorA/255;t.setGrid({enabled:i.enabled,color:`rgba(${i.colorR},${i.colorG},${i.colorB},${r})`,lineWidth:i.lineWidth}),this.log(`[Display ${e}] Grid ${i.enabled?"enabled":"disabled"}`)}}patternFromType(t){switch(t){case 0:return"horizontal";case 1:return"vertical";case 2:return"grid";default:return"horizontal"}}log(t){this.debug&&console.warn(`[PostProcessFeature] ${t}`)}};y(z,"PostProcessFeature");var K=z;import{ScalingMode as bt}from"@utsp/render";import{AudioManager as Ct}from"@utsp/audio";export{Q as AudioFeature,Ct as AudioManager,w as BaseClientRuntime,D as ClientRuntime,q as InputFeature,K as PostProcessFeature,j as RendererType,bt as ScalingMode};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@utsp/runtime-client",
3
- "version": "0.14.0-nightly.20251219144833.1c27cfe",
3
+ "version": "0.14.0-nightly.20251221141916.9580af2",
4
4
  "description": "UTSP Runtime Client - Local and multi-user client runtime",
5
5
  "author": "THP Software",
6
6
  "license": "MIT",
@@ -48,12 +48,12 @@
48
48
  "access": "public"
49
49
  },
50
50
  "dependencies": {
51
- "@utsp/audio": "0.14.0-nightly.20251219144833.1c27cfe",
52
- "@utsp/core": "0.14.0-nightly.20251219144833.1c27cfe",
53
- "@utsp/input": "0.14.0-nightly.20251219144833.1c27cfe",
54
- "@utsp/network-client": "0.14.0-nightly.20251219144833.1c27cfe",
55
- "@utsp/render": "0.14.0-nightly.20251219144833.1c27cfe",
56
- "@utsp/types": "0.14.0-nightly.20251219144833.1c27cfe"
51
+ "@utsp/audio": "0.14.0-nightly.20251221141916.9580af2",
52
+ "@utsp/core": "0.14.0-nightly.20251221141916.9580af2",
53
+ "@utsp/input": "0.14.0-nightly.20251221141916.9580af2",
54
+ "@utsp/network-client": "0.14.0-nightly.20251221141916.9580af2",
55
+ "@utsp/render": "0.14.0-nightly.20251221141916.9580af2",
56
+ "@utsp/types": "0.14.0-nightly.20251221141916.9580af2"
57
57
  },
58
58
  "devDependencies": {
59
59
  "typescript": "^5.6.3"