UNPKG

asciitorium

Version:
22 lines 94.7 kB
"use strict";var $t=Object.create;var bt=Object.defineProperty;var Nt=Object.getOwnPropertyDescriptor;var Yt=Object.getOwnPropertyNames;var Xt=Object.getPrototypeOf,Kt=Object.prototype.hasOwnProperty;var Gt=(n,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of Yt(t))!Kt.call(n,s)&&s!==e&&bt(n,s,{get:()=>t[s],enumerable:!(i=Nt(t,s))||i.enumerable});return n};var it=(n,t,e)=>(e=n!=null?$t(Xt(n)):{},Gt(t||!n||!n.__esModule?bt(e,"default",{value:n,enumerable:!0}):e,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("asciitorium/jsx-runtime");class O{constructor(){this.renderCallback=null,this.renderPending=!1}static getInstance(){return O.instance||(O.instance=new O),O.instance}setRenderCallback(t){this.renderCallback=t}requestRender(){this.renderPending||(this.renderPending=!0,queueMicrotask(()=>this.executeRender()))}executeRender(){this.renderPending=!1,this.renderCallback?.()}}const At=O.getInstance();function Mt(n){At.setRenderCallback(n)}function A(){At.requestRender()}function H(n){return typeof n=="object"&&typeof n.subscribe=="function"}function ct(){return typeof process<"u"&&typeof process.cpuUsage=="function"&&typeof process.memoryUsage=="function"}function j(){return typeof window<"u"&&typeof document<"u"}function kt(){if(!j())return!1;const n="ontouchstart"in window||navigator.maxTouchPoints>0,t=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),e=window.innerWidth<=768;return n||t&&e}function It(){return ct()}function Ft(){return ct()||typeof performance<"u"&&performance.memory}async function dt(n,t){if(j())window.addEventListener("keydown",e=>{e.key==="F1"&&e.preventDefault(),e.key==="Tab"&&e.shiftKey?n("Shift+Tab",e):n(e.key,e)}),kt()&&t&&(Jt(t),qt(n));else{const{default:e}=await import("readline");e.emitKeypressEvents(process.stdin),process.stdin.isTTY&&process.stdin.setRawMode(!0),process.stdin.on("keypress",(i,s)=>{const r=_t(s);n(r)})}}function Jt(n){const t=document.getElementById("mobile-controls"),e=document.getElementById("btn-menu");if(!t)return;t.classList.add("visible"),e&&e.classList.add("visible"),["btn-up","btn-down","btn-left","btn-right","btn-a","btn-b","btn-x","btn-y","btn-menu"].forEach(s=>{const r=document.getElementById(s);r&&(r.addEventListener("touchstart",o=>{o.preventDefault(),n(s)}),r.addEventListener("click",o=>{o.preventDefault(),n(s)}))})}function qt(n){const t=document.getElementById("screen");if(!t)return;let e=0,i=0,s=0;t.addEventListener("touchstart",r=>{e=r.touches[0].clientX,i=r.touches[0].clientY,s=Date.now()}),t.addEventListener("touchend",r=>{const o=r.changedTouches[0].clientX,h=r.changedTouches[0].clientY,a=Date.now(),c=o-e,l=h-i,u=a-s,g=50;u>500||Math.abs(c)>Math.abs(l)&&Math.abs(c)>g&&(c>0?n("Tab"):n("Shift+Tab"))})}function _t(n){if(!n)return"";switch(n.ctrl&&n.name==="c"&&process.exit(),n.name){case"return":return"Enter";case"escape":return"Escape";case"backspace":return"Backspace";case"tab":return n.shift?"Shift+Tab":"Tab";case"up":return"ArrowUp";case"down":return"ArrowDown";case"left":return"ArrowLeft";case"right":return"ArrowRight";default:return n.sequence||""}}function ut(){if(j()&&!document.getElementById("screen"))throw new Error("Missing #screen element for DOM renderer")}async function R(n){if(typeof window<"u"&&typeof fetch<"u"){let t=null,e=null;try{const i=await fetch(n);if(i.ok){const s=await i.text();if(s.trim().startsWith("<!doctype")||s.trim().startsWith("<!DOCTYPE")||s.trim().startsWith("<html"))t=new Error("HTML fallback (file not found)");else return s}else t=new Error(`${i.status} ${i.statusText}`)}catch(i){t=i}try{const i=`/lib-assets/${n}`,s=await fetch(i);if(s.ok){const r=await s.text();if(r.trim().startsWith("<!doctype")||r.trim().startsWith("<!DOCTYPE")||r.trim().startsWith("<html"))e=new Error("HTML fallback (file not found)");else return r}else e=new Error(`${s.status} ${s.statusText}`)}catch(i){e=i}throw new Error(`Failed to load ${n} - not found in project (${t?.message}) or library (${e?.message})`)}else{const{readFile:t}=await import("fs/promises"),{resolve:e}=await import("path"),i=e(process.cwd(),"public",n);try{return await t(i,"utf-8")}catch{}const s=e(process.cwd(),"node_modules","asciitorium","public",n);try{return await t(s,"utf-8")}catch{}throw new Error(`Failed to load ${n} - not found in project (${i}) or library (${s})`)}}async function Qt(n){ut(),await dt(t=>n.handleKey(t)),Mt(()=>n.render())}function T(n){if(typeof n=="number")return{top:n,bottom:n,left:n,right:n};if(Array.isArray(n)){if(n.length===1)return{top:n[0],bottom:n[0],left:n[0],right:n[0]};if(n.length===2)return{top:n[0],bottom:n[0],left:n[1],right:n[1]};if(n.length===3){const[t,e,i]=n;return{top:t,bottom:i,left:e,right:e}}else if(n.length>=4){const[t,e,i,s]=n;return{top:t,right:e,bottom:i,left:s}}else return{top:0,bottom:0,left:0,right:0}}if(typeof n=="object"&&n!==null){const t={top:n.top??0,bottom:n.bottom??0,left:n.left??0,right:n.right??0};return n.y!==void 0&&(t.top=n.y,t.bottom=n.y),n.x!==void 0&&(t.left=n.x,t.right=n.x),n.top!==void 0&&(t.top=n.top),n.bottom!==void 0&&(t.bottom=n.bottom),n.left!==void 0&&(t.left=n.left),n.right!==void 0&&(t.right=n.right),t}return{top:0,bottom:0,left:0,right:0}}function Zt(n){const t=n.match(/^(\d+(?:\.\d+)?)%$/);if(!t)throw new Error(`Invalid percentage format: ${n}`);return parseFloat(t[1])/100}function te(n){return typeof n=="string"&&n.endsWith("%")}function wt(n,t,e){if(n!==void 0){if(typeof n=="number")return n;if(te(n)){const i=Zt(n),s=e==="width"?t.availableWidth:t.availableHeight;return Math.floor(s*i)}if(n!=="auto"){if(n==="fill")return e==="width"?t.availableWidth:t.availableHeight;throw new Error(`Unknown size value: ${n}`)}}}function V(n,t,e=0){const i=n-2*e,s=t-2*e;return{parentWidth:n,parentHeight:t,availableWidth:Math.max(0,i),availableHeight:Math.max(0,s)}}class ee{constructor(t){}layout(t,e){const i=t.border?1:0,s=t.height-2*i,r=t.width-2*i,o=e.filter(d=>!(d.width===0||d.height===0||d.visible!==void 0&&!d.visible)),h=[];let a=0;for(const d of o){if(d.fixed){const S=V(t.width,t.height,i);d.resolveSize(S);continue}const m=T(d.gap);if(d.getOriginalWidth()==="fill")h.push(d),a+=m.left+m.right;else{const S=V(t.width,t.height,i);d.resolveSize(S),a+=m.left+d.width+m.right}}const c=r-a,l=h.length;if(l>0&&c>0){const d=Math.floor(c/l);for(const m of h){const x=T(m.gap);m.width=Math.max(1,d-x.left-x.right)}}else if(l>0)for(const d of h)d.width=1;let u=0;for(const d of o){if(d.fixed)continue;const m=T(d.gap);u+=m.left+d.width+m.right}const g=r-u;let p=0,y="top";if(t.align){let d=t.align;t.align==="left"&&(d="center-left"),t.align==="right"&&(d="center-right"),t.align==="top"&&(d="top-center"),t.align==="bottom"&&(d="bottom-center");const[m,x]=d.split("-").length===2?d.split("-"):d==="center"?["center","center"]:["top","left"];x==="center"?p=Math.floor(g/2):x==="right"&&(p=g),y=m}let w=i+p;for(const d of o){if(d.fixed)continue;const m=T(d.gap);w+=m.left;const x=s-m.top-m.bottom,S=d.getOriginalHeight();if(S==="fill")d.height=Math.max(1,x);else if(S===void 0){const D=V(t.width,t.height,i);d.resolveSize(D)}let C=0;y==="center"?C=Math.floor((x-d.height)/2):y==="bottom"&&(C=x-d.height),d.x=w,d.y=i+m.top+C,w+=d.width+m.right}}}class ie{constructor(t){}layout(t,e){const i=t.border?1:0,s=t.width-2*i,r=t.height-2*i,o=e.filter(d=>!(d.width===0||d.height===0||d.visible!==void 0&&!d.visible)),h=[];let a=0;for(const d of o){if(d.fixed){const S=V(t.width,t.height,i);d.resolveSize(S);continue}const m=T(d.gap);if(d.getOriginalHeight()==="fill")h.push(d),a+=m.top+m.bottom;else{const S=V(t.width,t.height,i);d.resolveSize(S),a+=m.top+d.height+m.bottom}}const c=r-a,l=h.length;if(l>0&&c>0){const d=Math.floor(c/l);for(const m of h){const x=T(m.gap);m.height=Math.max(1,d-x.top-x.bottom)}}else if(l>0)for(const d of h)d.height=1;let u=0;for(const d of o){if(d.fixed)continue;const m=T(d.gap);u+=m.top+d.height+m.bottom}const g=r-u;let p=0,y="left";if(t.align){let d=t.align;t.align==="left"&&(d="center-left"),t.align==="right"&&(d="center-right"),t.align==="top"&&(d="top-center"),t.align==="bottom"&&(d="bottom-center");const[m,x]=d.split("-").length===2?d.split("-"):d==="center"?["center","center"]:["top","left"];m==="center"?p=Math.floor(g/2):m==="bottom"&&(p=g),y=x}let w=i+p;for(const d of o){if(d.fixed)continue;const m=T(d.gap);w+=m.top;const x=s-m.left-m.right;d.getOriginalWidth()==="fill"&&(d.width=Math.max(1,x));let C=0;y==="center"?C=Math.floor((x-d.width)/2):y==="right"&&(C=x-d.width),d.x=i+m.left+C,d.y=w,w+=d.height+m.bottom}}}const Y=class Y{static register(t,e){this.layouts.set(t,e)}static create(t,e){const i=this.layouts.get(t);if(!i)throw new Error(`Unknown layout type: ${t}`);return new i(e)}static getAvailableTypes(){return Array.from(this.layouts.keys())}};Y.layouts=new Map,Y.layouts.set("row",ee),Y.layouts.set("column",ie);let nt=Y;const W={topLeft:"╭",topRight:"╮",bottomLeft:"╰",bottomRight:"╯",horizontal:"─",vertical:"│"};function se(n){const t=n.style||{};return{width:n.width??t.width,height:n.height??t.height,border:n.border??t.border,background:n.background??t.background,align:n.align??t.align,position:n.position??t.position,gap:n.gap??t.gap,font:t.font,layout:t.layout}}const X=class X{constructor(t){this.showLabel=!0,this.fixed=!1,this.x=0,this.y=0,this.z=0,this.gap=0,this.focusable=!1,this.hasFocus=!1,this.captureModeActive=!1,this.transparentChar="‽",this.unbindFns=[],this.cleanupFns=[],this.children=[];const e=se(t);this.originalWidth=e.width,this.originalHeight=e.height,typeof e.width=="number"?this.width=e.width:this.width=1,typeof e.height=="number"?this.height=e.height:this.height=1,this.label=t.label,this.comment=t.comment,this.showLabel=t.showLabel??!0,this.border=e.border??!1,this.fill=e.background??" ",this.align=e.align,e.position?(this.x=e.position.x??0,this.y=e.position.y??0,this.z=e.position.z??0,this.fixed=!0):(this.x=0,this.y=0,this.z=0,this.fixed=!1),this.gap=e.gap??0,this.hotkey=t.hotkey,this.buffer=[],this.visibleState=t.visible,this.layoutType=t.layout??"column",this.layoutOptions=t.layoutOptions,this.initializeChildren(t)}setParent(t){this.parent=t}get visible(){return this.visibleState?.value??!0}initializeChildren(t){if(!t.children)return;const e=Array.isArray(t.children)?t.children:[t.children];for(const i of e)this.isValidChild(i)&&(i.setParent(this),this.children.push(i));this.recalculateLayout()}isValidChild(t){return t&&typeof t=="object"&&typeof t.setParent=="function"}addChild(t){t.setParent(this),this.children.push(t),this.recalculateLayout(),A()}removeChild(t){const e=this.children.indexOf(t);e!==-1&&(this.children.splice(e,1),this.recalculateLayout(),A())}getChildren(){return this.children}setChildren(t){for(const e of t)this.isValidChild(e)&&(e.setParent(this),this.children.push(e));this.recalculateLayout(),A()}getAllDescendants(){const t=[];for(const e of this.children){t.push(e);const i=e.getAllDescendants();t.push(...i)}return t}invalidateLayout(){this.layout=void 0}recalculateLayout(){this.children.length!==0&&(this.layout||(this.layout=nt.create(this.layoutType,this.layoutOptions)),this.layout.layout(this,this.children),this.recalculateAutoSize())}recalculateAutoSize(){let t=!1;if(this.originalWidth===void 0&&this.children.length>0){const e=X.calculateAutoWidth(this.children,this.layoutType),i=this.border?2:0,s=Math.max(1,e+i);s!==this.width&&(this.width=s,t=!0)}if(this.originalHeight===void 0&&this.children.length>0){const e=X.calculateAutoHeight(this.children,this.layoutType),i=this.border?2:0,s=Math.max(1,e+i);s!==this.height&&(this.height=s,t=!0)}t&&this.parent&&this.parent.recalculateLayout()}bind(t,e){const i=s=>{e(s)};t.subscribe(i),this.unbindFns.push(()=>t.unsubscribe(i))}registerCleanup(t){this.cleanupFns.push(t)}destroy(){const t=[...this.children];for(const e of t)e.destroy();for(const e of this.cleanupFns)try{e()}catch(i){console.error("Error during cleanup:",i)}this.cleanupFns=[];for(const e of this.unbindFns)e();this.unbindFns=[]}static calculateAutoWidth(t,e){return!t||t.length===0?1:e==="row"?t.reduce((i,s)=>{const r=T(s.gap);return i+s.width+r.left+r.right},0):Math.max(...t.map(i=>{const s=T(i.gap);return i.width+s.left+s.right}))}static calculateAutoHeight(t,e){return!t||t.length===0?1:e==="column"?t.reduce((i,s)=>{const r=T(s.gap);return i+s.height+r.top+r.bottom},0):Math.max(...t.map(i=>{const s=T(i.gap);return i.height+s.top+s.bottom}))}notifyAppOfFocusRefresh(){let t=this;for(;t&&!t.isApp;)t=t.parent;t&&t.focus&&t.focus.refresh(t)}notifyAppOfFocusReset(){let t=this;for(;t&&!t.isApp;)t=t.parent;t&&t.focus&&t.focus.reset(t)}getOriginalWidth(){return this.originalWidth}getOriginalHeight(){return this.originalHeight}resolveSize(t){if(this.originalWidth!==void 0){const e=wt(this.originalWidth,t,"width");e!==void 0&&(this.width=e)}if(this.originalHeight!==void 0){const e=wt(this.originalHeight,t,"height");e!==void 0&&(this.height=e)}this.width<1&&(this.width=1),this.height<1&&(this.height=1)}handleEvent(t){return!1}drawBorder(t){const e=this.width,i=this.height;t(0,0,W.topLeft),t(e-1,0,W.topRight),t(0,i-1,W.bottomLeft),t(e-1,i-1,W.bottomRight);for(let s=1;s<e-1;s++)t(s,0,W.horizontal),t(s,i-1,W.horizontal);for(let s=1;s<i-1;s++)t(0,s,W.vertical),t(e-1,s,W.vertical)}renderChildren(){const t=[...this.children].sort((e,i)=>e.z-i.z);for(const e of t)this.compositeChildBuffer(e)}compositeChildBuffer(t){const e=t.draw();for(let i=0;i<e.length;i++)for(let s=0;s<e[i].length;s++){const r=t.x+s,o=t.y+i;if(r>=0&&r<this.width&&o>=0&&o<this.height){if(!this.buffer[o])continue;const h=e[i][s];h!==t.transparentChar&&(this.border&&(o===0||o===this.height-1||r===0||r===this.width-1)||r<this.buffer[o].length&&(this.buffer[o][r]=h))}}}isHotkeyVisibilityEnabled(){let t=this;for(;t&&!t.isApp;)t=t.parent;return t&&t.focus?t.focus.hotkeyVisibilityState.value:!1}draw(){if(!this.visible)return[];this.recalculateLayout(),this.buffer=Array.from({length:this.height},()=>Array.from({length:this.width},()=>this.fill===this.transparentChar?"‽":this.fill));const t=(e,i,s)=>{e>=0&&e<this.width&&i>=0&&i<this.height&&(this.buffer[i][e]=s)};if(this.border&&this.drawBorder(t),this.label&&this.showLabel){const e=` ${this.label} `,i=Math.max(1,Math.floor((this.width-e.length)/2));for(let s=0;s<e.length&&s+i<this.width-1;s++)t(s+i,0,e[s])}if(this.border&&this.hotkey&&this.isHotkeyVisibilityEnabled()){const e=`[${this.hotkey.toUpperCase()}]`;for(let i=0;i<e.length&&i+1<this.width-1;i++)t(i+1,0,e[i])}return this.renderChildren(),this.buffer}};X.BYPASS_KEYS=["Tab","Shift+Tab"];let v=X;class M{constructor(t){this.listeners=[],this._value=t}get value(){return this._value}set value(t){this._value!==t&&(this._value=t,this.listeners.forEach(e=>e(t)))}subscribe(t){this.listeners.push(t),t(this._value)}unsubscribe(t){this.listeners=this.listeners.filter(e=>e!==t)}}const z=class z{constructor(){this.contextStack=[],this.index=0,this.hotkeyMap=new Map,this.hotkeyVisibilityState=new M(!1)}pushContext(t){this.clearFocus();const e=t.filter(i=>i.focusable);this.contextStack.push(e),this.index=0,this.setFocus(0)}popContext(){this.clearFocus(),this.contextStack.pop(),this.index=0,this.setFocus(0)}get currentContext(){return this.contextStack[this.contextStack.length-1]||[]}setFocus(t){this.index=t;const e=this.currentContext[this.index];e&&(e.hasFocus=!0)}clearFocus(){const t=this.currentContext[this.index];t&&(t.hasFocus=!1)}focusNext(){this.currentContext.length!==0&&(this.clearFocus(),this.index=(this.index+1)%this.currentContext.length,this.setFocus(this.index))}focusPrevious(){this.currentContext.length!==0&&(this.clearFocus(),this.index=(this.index-1+this.currentContext.length)%this.currentContext.length,this.setFocus(this.index))}handleKey(t){const e=this.currentContext[this.index];if(e?.captureModeActive&&!v.BYPASS_KEYS.includes(t))return e.handleEvent?.(t)??!1;if(t==="`"||t==="F1")return this.hotkeyVisibilityState.value=!this.hotkeyVisibilityState.value,!0;const i=this.hotkeyMap.get(t.toLowerCase());if(i){const r=this.currentContext.indexOf(i);if(r!==-1)return this.clearFocus(),this.index=r,this.setFocus(r),i.onClick&&i.onClick(),!0}return t==="Tab"?(this.focusNext(),!0):t==="Shift+Tab"?(this.focusPrevious(),!0):e?.handleEvent?.(t)??!1}reset(t){const e=this.getFocusableDescendants(t).filter(i=>i.focusable);this.contextStack=[e],this.index=0,this.setFocus(0),this.buildHotkeyMap(e)}refresh(t){const e=this.currentContext[this.index],i=this.getFocusableDescendants(t).filter(s=>s.focusable);if(this.contextStack=[i],this.buildHotkeyMap(i),e){const s=i.indexOf(e);if(s!==-1){this.index=s,this.setFocus(s);return}}this.index=0,this.setFocus(0)}buildHotkeyMap(t){this.hotkeyMap.clear();for(const e of t)if(e.hotkey)if(this.isReservedKey(e.hotkey))console.error(`ERROR: Hotkey '${e.hotkey}' is reserved for system navigation and cannot be used. Reserved keys: ${Array.from(z.RESERVED_KEYS).join(", ")}`);else{const i=e.hotkey.toLowerCase();this.hotkeyMap.has(i)?console.warn(`Hotkey '${e.hotkey}' is already assigned to another component`):this.hotkeyMap.set(i,e)}}isReservedKey(t){return z.RESERVED_KEYS.has(t)||z.RESERVED_KEYS.has(t.toLowerCase())}getFocusableDescendants(t){const e=[];for(const i of t.getChildren())i.visible&&(i.focusable&&e.push(i),e.push(...this.getFocusableDescendants(i)));return e}};z.RESERVED_KEYS=new Set(["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Enter"," ","Escape","Backspace","Delete","Home","End","PageUp","PageDown"]);let ot=z;class re{constructor(t,e){this.screen=t,this.charWidth=0,this.charHeight=0,e&&(this.screen.style.whiteSpace="pre",this.screen.style.fontFamily=e),setTimeout(()=>this.measureCharacterSize(),10)}measureCharacterSize(){const t=document.createElement("span");t.style.position="absolute",t.style.visibility="hidden",t.style.whiteSpace="pre",t.style.fontFamily=this.screen.style.fontFamily,t.style.fontSize=getComputedStyle(this.screen).fontSize||"16px",t.textContent="M",document.body.appendChild(t);const e=t.getBoundingClientRect();this.charWidth=e.width,this.charHeight=e.height,document.body.removeChild(t),this.charWidth===0&&(this.charWidth=8),this.charHeight===0&&(this.charHeight=16)}getScreenSize(){(this.charWidth===0||this.charHeight===0)&&this.measureCharacterSize();const t=this.screen.getBoundingClientRect(),e=t.width||window.innerWidth,i=t.height||window.innerHeight,s=Math.floor(e/this.charWidth),r=Math.floor(i/this.charHeight);return{width:Math.max(s,80),height:Math.max(r,24)}}render(t){this.screen.textContent=t.map(e=>e.join("")).join(`
`)}}class ne{getScreenSize(){let t=80,e=24;if(process.stdout.isTTY)t=process.stdout.columns||80,e=process.stdout.rows||24;else try{const i=require("tty");if(i.isatty(1)){const s=i.getWindowSize();s&&s.length>=2&&(t=s[0],e=s[1])}}catch{t=parseInt(process.env.COLUMNS||"80",10),e=parseInt(process.env.LINES||"24",10)}return{width:t,height:Math.max(1,e-1)}}render(t){const e=t.map(i=>i.join("")).join(`
`);process.stdout.write("\x1B[2J"),process.stdout.write("\x1B[H"),process.stdout.write("\x1B[38;2;44;208;58m"),process.stdout.write(e+`
`),process.stdout.write("\x1B[0m")}}class Tt extends v{constructor(t){const e=t.style?.font,i=t.font??e,s=oe(i),r=s.getScreenSize(),o=t.style?.width,h=t.style?.height,a=t.style?.layout,c=typeof(t.width??o)=="number",l=typeof(t.height??h)=="number",u={...t,width:t.width??o??r.width,height:t.height??h??r.height,layout:t.layout??a??"column",layoutOptions:t.layoutOptions??{}};super(u),this.isApp=!0,this.fpsCounter=0,this.totalRenderTime=0,this.currentFPS=0,this.currentCPU=0,this.currentMemory=0,this.keybindRegistry=new Map,this.mobileControllerRegistry=[],this.renderer=s,this.focus=new ot,this.fixedWidth=c,this.fixedHeight=l,this.setupResizeHandling(),this.focus.reset(this),this.focus.hotkeyVisibilityState.subscribe(()=>{this.render()}),this.render(),this.updatePerformanceMetrics();const g=setInterval(()=>{this.currentFPS=this.fpsCounter,this.fpsCounter=0,this.totalRenderTime=0,this.updatePerformanceMetrics()},1e3);this.registerCleanup(()=>clearInterval(g))}render(){const t=typeof performance<"u"&&performance.now?performance.now():Date.now();this.fpsCounter++;const e=Array.from({length:this.height},()=>Array.from({length:this.width},()=>" ")),i=this.getAllDescendants().concat([this]);i.sort((r,o)=>(r.z??0)-(o.z??0));for(const r of i){if(!r.visible)continue;const o=r.draw(),h=r.x,a=r.y,c=r.transparentChar;for(let l=0;l<o.length;l++){const u=a+l;if(!(u<0||u>=this.height))for(let g=0;g<o[l].length;g++){const p=h+g;if(p<0||p>=this.width)continue;const y=o[l][g];y!==c&&(e[u][p]=y)}}}this.renderer.render(e);const s=typeof performance<"u"&&performance.now?performance.now():Date.now();this.totalRenderTime+=s-t}addChild(t){super.addChild(t),this.focus?.reset(this),this.render()}removeChild(t){super.removeChild(t),this.focus.reset(this),this.render()}getFPS(){return this.currentFPS}getRenderTime(){return this.totalRenderTime}getCPUUsage(){return this.currentCPU}getMemoryUsage(){return this.currentMemory}updatePerformanceMetrics(){if(typeof process<"u"&&process.cpuUsage&&process.memoryUsage)try{const t=process.cpuUsage(this.lastCPUUsage),e=t.user+t.system;this.currentCPU=Math.min(100,e/1e4),this.lastCPUUsage=process.cpuUsage();const i=process.memoryUsage();this.currentMemory=i.heapUsed/(1024*1024)}catch{this.currentCPU=0,this.currentMemory=0}else if(typeof performance<"u"&&performance.memory)try{const t=performance.memory;this.currentMemory=t.usedJSHeapSize/(1024*1024),this.currentCPU=Math.min(100,Math.max(0,this.totalRenderTime*6))}catch{this.currentCPU=0,this.currentMemory=0}else this.currentCPU=0,this.currentMemory=0}registerKeybind(t){this.keybindRegistry.has(t.keyBinding)&&console.warn(`Warning: Duplicate keybinding for "${t.keyBinding}". The previous binding will be overwritten.`),this.keybindRegistry.set(t.keyBinding,t)}unregisterKeybind(t){this.keybindRegistry.delete(t.keyBinding)}registerMobileController(t){this.mobileControllerRegistry.push(t)}unregisterMobileController(t){const e=this.mobileControllerRegistry.indexOf(t);e!==-1&&this.mobileControllerRegistry.splice(e,1)}handleMobileButton(t){const e=this.mobileControllerRegistry.filter(i=>!i.disabled&&this.isComponentInTree(i)).sort((i,s)=>s.priority-i.priority);for(const i of e)if(i.handleButton(t)){this.render();return}}handleKey(t,e){const i=this.getFocusedComponent();if(i?.captureModeActive&&!v.BYPASS_KEYS.includes(t)){i.handleEvent(t)&&(this.render(),e?.preventDefault());return}const s=this.keybindRegistry.get(t);if(s&&!s.disabled&&this.isKeybindActive(s)){s.action(),this.render(),e?.preventDefault();return}this.focus.handleKey(t)&&(this.render(),e?.preventDefault())}getFocusedComponent(){return this.getAllDescendants().find(t=>t.hasFocus)}isKeybindActive(t){return this.isComponentInTree(t)}isComponentInTree(t){let e=t;for(;e;){if(e===this)return!0;e=e.parent}return!1}hasComponentWithFocus(){return this.getAllDescendants().some(t=>t.hasFocus)}setupResizeHandling(){const t=()=>{const e=this.renderer.getScreenSize();let i=!1;!this.fixedWidth&&e.width!==this.width&&(this.width=e.width,i=!0),!this.fixedHeight&&e.height!==this.height&&(this.height=e.height,i=!0),i&&this.render()};typeof window<"u"&&window.addEventListener("resize",t),typeof process<"u"&&process.on&&process.on("SIGWINCH",t)}getScreenSize(){return this.renderer.getScreenSize()}async start(){ut(),await dt((t,e)=>this.handleKey(t,e),t=>this.handleMobileButton(t)),Mt(()=>this.render()),this.render()}resolveSizesRecursively(){console.log("=== PASS 1: Size Resolution Starting ==="),this.resolveSizesForComponent(this),console.log("=== PASS 1: Size Resolution Complete ==="),this.validateSizesResolved()}validateSizesResolved(){console.log("=== Validating All Sizes Are Resolved ===");const t=this.getAllDescendants().concat([this]);let e=0;for(const i of t){const s=i.constructor.name;(typeof i.width!="number"||i.width<1||typeof i.height!="number"||i.height<1)&&i.visible&&(console.error(`❌ ${s} has invalid dimensions: ${i.width}x${i.height}, originalWidth=${i.originalWidth}, originalHeight=${i.originalHeight}, visible=${i.visible}`),e++)}e===0?console.log("✅ All visible components have valid numeric dimensions"):console.error(`❌ ${e} visible components have invalid dimensions!`)}resolveSizesForComponent(t){if(t.constructor.name,t.width,t.height,t.parent){const s=t.parent.border?1:0,r=V(t.parent.width,t.parent.height,s);t.resolveSize(r)}else{const s=V(this.width,this.height,0);t.resolveSize(s)}const e=t.children||[];e.filter(s=>s.visible).length>0&&(t.height,t.recalculateLayout());for(const s of e)this.resolveSizesForComponent(s)}}function oe(n){if(typeof window<"u"&&typeof document<"u"){const t=document.getElementById("screen");if(!t)throw new Error("No #screen element found for DOM rendering");return new re(t,n)}else return new ne}class tt extends M{constructor(t,e){const i=tt.loadFromStorage(e,t);super(i),this.storageKey=e}get value(){return super.value}set value(t){super.value=t,this.saveToStorage(t)}saveToStorage(t){if(j())try{const e=`asciitorium-${this.storageKey}`;localStorage.setItem(e,JSON.stringify(t))}catch(e){console.warn("Failed to save state to localStorage:",e)}}static loadFromStorage(t,e){if(j())try{const i=`asciitorium-${t}`,s=localStorage.getItem(i);if(s!==null)return JSON.parse(s)}catch(i){console.warn("Failed to load state from localStorage:",i)}return e}clearStorage(){if(j())try{const t=`asciitorium-${this.storageKey}`;localStorage.removeItem(t)}catch(t){console.warn("Failed to clear state from localStorage:",t)}}}class ft{calculateScrollWindow(t){const{items:e,totalCount:i,maxVisible:s,focusedIndex:r}=t,o=Math.max(0,Math.min(r-Math.floor(s/2),Math.max(0,i-s))),h=e.slice(o,o+s);return{startIdx:o,visibleItems:h,showUpArrow:o>0,showDownArrow:o+s<i}}handleScrollEvent(t,e,i){let s=e;return t==="ArrowUp"&&e>0?s=e-1:t==="ArrowDown"&&e<i-1&&(s=e+1),s}drawScrollIndicators(t,e,i,s,r,o){if(r){const h=s,a=e-2;h>=0&&h<i&&a>=0&&a<e&&(t[h][a]="↑")}if(o){const h=i-1-s,a=e-2;h>=0&&h<i&&a>=0&&a<e&&(t[h][a]="↓")}}}const U=class U{static async getMap(t){if(this.mapCache.has(t))return this.mapCache.get(t);const e=this.loadMapAsset(t);return this.mapCache.set(t,e),e}static async getMaterial(t){if(this.materialCache.has(t))return this.materialCache.get(t);const e=this.loadMaterialAsset(t);return this.materialCache.set(t,e),e}static async getSprite(t){if(this.spriteCache.has(t))return this.spriteCache.get(t);const e=this.loadSpriteAsset(t);return this.spriteCache.set(t,e),e}static async getFont(t){if(this.fontCache.has(t))return this.fontCache.get(t);const e=this.loadFontAsset(t);return this.fontCache.set(t,e),e}static calculateMapWidth(t){return Math.max(...t.mapData.map(e=>e.length))}static calculateMaterialWidth(t){let e=0;for(const i of t.layers){const s=Math.max(...i.lines.map(r=>r.length));e=Math.max(e,s)}return e}static calculateMaterialHeight(t){let e=0;for(const i of t.layers)e=Math.max(e,i.lines.length);return e}static calculateSpriteWidth(t){let e=0;for(const i of t.frames)for(const s of i.lines){const r=s.length;e=Math.max(e,r)}return e}static calculateSpriteHeight(t){let e=0;for(const i of t.frames)e=Math.max(e,i.lines.length);return e}static async loadMapAsset(t){try{const[e,i]=await Promise.all([R(`art/maps/${t}/map.art`),R(`art/maps/${t}/legend.json`)]),s=e.split(`
`),r=JSON.parse(i),o=this.expandLegendFormat(r);return{mapData:s,legend:o}}catch(e){const i=e instanceof Error?e.message:"Unknown error";throw new Error(`Failed to load map "${t}": ${i}`)}}static expandLegendFormat(t){const e={};if(t.legend&&Array.isArray(t.legend))for(const i of t.legend){if(!i.chars||!Array.isArray(i.chars)){console.warn("Legend entry missing chars array:",i);continue}const s={solid:i.solid,material:i.material,...i.name&&{name:i.name},...i.showOnMap!==void 0&&{showOnMap:i.showOnMap},...i.entity&&{entity:i.entity},...i.variant&&{variant:i.variant}};for(const r of i.chars)e[r]=s}else console.warn('Invalid legend format: expected { "legend": [...] }');return e}static async loadMaterialAsset(t){try{const e=await R(`art/materials/${t}.art`);return this.parseMaterialData(e)}catch(e){const i=e instanceof Error?e.message:"Unknown error";throw new Error(`Failed to load material "${t}": ${i}`)}}static async loadSpriteAsset(t){try{const e=await R(`art/sprites/${t}.art`);return this.parseSpriteData(e)}catch(e){const i=e instanceof Error?e.message:"Unknown error";throw new Error(`Failed to load sprite "${t}": ${i}`)}}static async loadFontAsset(t){try{const e=await R(`art/fonts/${t}.art`);return this.parseFontData(e)}catch(e){const i=e instanceof Error?e.message:"Unknown error";throw new Error(`Failed to load font "${t}": ${i}`)}}static parseMaterialData(t){const e=t.split("§");if(e.length<2)throw new Error("Invalid material file format: missing § header");const i=e[1],s=i.split(`
`),r=s[0].trim();let o,h=!1;try{if(o=JSON.parse(r),o.artType==="fpsScenery"&&o.layer)h=!0;else if(o.kind==="material")h=!1;else throw new Error("File is not a material or scene")}catch(c){const l=c instanceof Error?c.message:"Unknown error";throw new Error(`Failed to parse file metadata: ${l}`)}const a=[];if(h){const l=i.substring(s[0].length+1).split(`
`);let u=-1;for(let p=0;p<l.length;p++)if(l[p].trim().startsWith("¶")){u=p;break}const g=u===-1?l:l.slice(0,u);if(a.push({layer:o.layer,position:o.position,x:o.x,lines:g}),u!==-1){const p=l.slice(u).join(`
`).split("¶");for(const y of p)y.trim()&&this.parseLayerSection(y,a)}}else{const l=i.substring(s[0].length+1).split("¶");for(const u of l)u.trim()&&this.parseLayerSection(u,a)}return{usage:o.usage||(h?"first-person":"unknown"),placement:o.placement,onEnterSound:o.onEnterSound,onExitSound:o.onExitSound,ambientSound:o.ambientSound,layers:a}}static parseLayerSection(t,e){const i=t.split(`
`);if(i.length===0)return;const s=i[0].trim();if(s.startsWith("{"))try{const r=JSON.parse(s);if(r.layer&&r.position){const o=i.slice(1);e.push({layer:r.layer,position:r.position,x:r.x,lines:o})}}catch(r){console.warn("Failed to parse layer metadata:",s,r)}}static parseSpriteData(t){const e=[];let i={};const s=t.split("§");if(s.length<2)return{frames:[{lines:this.normalizeBlock(t.split(`
`)),meta:{}}],defaults:{}};const r=s[1],o=r.split(`
`),h=o[0].trim();try{const l=JSON.parse(h);l.kind==="sprite"&&(i={duration:l["default-frame-rate"]||l.duration,loop:l.loop,transparent:typeof l.transparent=="string"&&l.transparent.length===1?l.transparent:void 0})}catch(l){console.warn("Failed to parse sprite file metadata:",h,l)}const c=r.substring(o[0].length+1).split("¶");if(c[0]&&c[0].trim()){const l=c[0].split(`
`);e.push({lines:this.normalizeBlock(l),meta:{duration:i.duration}})}for(let l=1;l<c.length;l++){const u=c[l];if(!u.trim())continue;const g=u.split(`
`);if(g.length===0)continue;const p=g[0].trim();let y={duration:i.duration};if(p.startsWith("{")){try{const d=JSON.parse(p);y={duration:d.duration||i.duration,...d.sound&&{sound:d.sound}}}catch(d){console.warn("Failed to parse frame metadata:",p,d)}const w=g.slice(1);e.push({lines:this.normalizeBlock(w),meta:y})}else e.push({lines:this.normalizeBlock(g),meta:y})}return{frames:e,defaults:i}}static parseFontData(t){const e=new Map;let i=0;const s=t.split("§");if(s.length<2)throw new Error("Invalid font file format: missing § header");const r=s[1],o=r.split(`
`),h=o[0].trim();try{if(JSON.parse(h).kind!=="font")throw new Error("File is not a font")}catch(l){const u=l instanceof Error?l.message:"Unknown error";throw new Error(`Failed to parse font file metadata: ${u}`)}const c=r.substring(o[0].length+1).split("¶");for(let l=1;l<c.length;l++){const u=c[l];if(!u.trim())continue;const g=u.split(`
`);if(g.length===0)continue;const p=g[0].trim();if(p.startsWith("{"))try{const y=JSON.parse(p);if(!y.character){console.warn("Glyph metadata missing character property:",p);continue}const w=Array.isArray(y.character)?y.character:[y.character];if(!w.every(C=>typeof C=="string")){console.warn("Glyph character must be string or array of strings:",p);continue}const d=this.normalizeFontBlock(g.slice(1)),m=d.length,x=Math.max(...d.map(C=>C.length),0),S={character:w[0],lines:d,width:x,height:m};for(const C of w)e.set(C,S);i=Math.max(i,m)}catch(y){console.warn("Failed to parse glyph metadata:",p,y)}}if(e.size===0)throw new Error("No valid glyphs found in font file");return{glyphs:e,height:i}}static normalizeBlock(t){const e=t.slice();return e.length&&e[0]===""&&e.shift(),e.map(s=>[...s])}static normalizeFontBlock(t){return t.map(i=>[...i])}};U.mapCache=new Map,U.materialCache=new Map,U.spriteCache=new Map,U.fontCache=new Map;let P=U;const K=class K{static setEnabled(t){this.enabled=t}static isEnabled(){return this.enabled}static async playSound(t,e=!1){if(j()&&this.enabled)try{let i=this.audioCache.get(t);i||(i=new Audio(`art/sounds/${t}`),this.audioCache.set(t,i));const s=i.cloneNode();s.loop=e,e&&(this.playingAudio.set(t,s),s.addEventListener("ended",()=>{this.playingAudio.delete(t)})),await s.play()}catch(i){console.warn(`Failed to play sound "${t}":`,i)}}static fadeToStop(t,e=1e3){if(!j())return;const i=this.playingAudio.get(t);if(!i)return;const s=i.volume,r=Date.now(),o=setInterval(()=>{const h=Date.now()-r,a=Math.min(h/e,1);i.volume=s*(1-a),a>=1&&(clearInterval(o),i.pause(),i.currentTime=0,this.playingAudio.delete(t))},16)}static clearCache(){this.audioCache.clear()}};K.audioCache=new Map,K.playingAudio=new Map,K.enabled=!0;let G=K;class gt{constructor(t){this.previousPosition=null,this.mapState=t.mapAsset,this.playerState=t.player,this.previousPosition={x:t.player.value.x,y:t.player.value.y}}getPlayerState(){return this.playerState}getPlayer(){return this.playerState.value}getMapState(){return this.mapState}getMapData(){return this.mapState.value?.mapData??[]}getLegend(){return this.mapState.value?.legend??{}}isReady(){return this.mapState.value!==null}getLegendEntry(t){return this.getLegend()[t]}getCharAt(t,e){const i=this.getMapData();if(!(e<0||e>=i.length)&&!(t<0||t>=i[e].length))return i[e][t]}isSolidChar(t){return this.getLegend()[t]?.solid??!1}isSolid(t,e){const i=this.getCharAt(t,e);return i===void 0?!0:this.isSolidChar(i)}moveForward(){const t=this.getPlayer(),{dx:e,dy:i}=this.getDirectionVector(t.direction);return this.movePlayerBy(e,i,t.direction)}moveBackward(){const t=this.getPlayer(),e=this.getOppositeDirection(t.direction),{dx:i,dy:s}=this.getDirectionVector(e);return this.movePlayerBy(i,s,t.direction)}turnLeft(){const t=this.getPlayer(),e=this.getNewDirection(t.direction,"left");this.playerState.value={...t,direction:e}}turnRight(){const t=this.getPlayer(),e=this.getNewDirection(t.direction,"right");this.playerState.value={...t,direction:e}}getDirectionVector(t){switch(t){case"north":return{dx:0,dy:-1};case"south":return{dx:0,dy:1};case"east":return{dx:2,dy:0};case"west":return{dx:-2,dy:0}}}getOppositeDirection(t){switch(t){case"north":return"south";case"south":return"north";case"east":return"west";case"west":return"east"}}getNewDirection(t,e){const i=["north","east","south","west"],o=(i.indexOf(t)+(e==="left"?-1:1)+4)%4;return i[o]}movePlayerBy(t,e,i){const s=this.getPlayer(),r=this.getMapData(),o=r.length,h=o>0?r[0].length:0;if(h===0||o===0)return!1;const a=Math.max(0,Math.min(h-1,s.x+t)),c=Math.max(0,Math.min(o-1,s.y+e));if(Math.abs(t)===2){const l=t>0?1:-1,u=s.x+l;if(this.isSolid(u,s.y))return!1}return this.isSolid(a,c)?!1:(this.previousPosition&&this.checkAndPlayExitSound(this.previousPosition.x,this.previousPosition.y),this.playerState.value={x:a,y:c,direction:i},this.previousPosition={x:a,y:c},this.checkAndPlayTileSound(a,c),!0)}async checkAndPlayTileSound(t,e){const i=this.getCharAt(t,e);if(!i)return;const s=this.getLegendEntry(i);if(s)try{const r=await P.getMaterial(s.material);r.onEnterSound&&G.playSound(r.onEnterSound)}catch(r){console.debug("Could not check tile sound:",r)}}async checkAndPlayExitSound(t,e){const i=this.getCharAt(t,e);if(!i)return;const s=this.getLegendEntry(i);if(s)try{const r=await P.getMaterial(s.material);r.onExitSound&&G.playSound(r.onExitSound)}catch(r){console.debug("Could not check exit sound:",r)}}}class pt extends v{constructor(t){let e=t.content;const i=t.border?2:0,s=!!t.sprite;let r=[],o=!1,h,a,c;if(s)r=[{lines:[["L","o","a","d","i","n","g",".",".","."]],meta:{duration:0}}],o=!1,a=12,c=1+i;else{if(!e&&t.children){const x=Array.isArray(t.children)?t.children:[t.children];x.length>0&&(e=x[0])}if(!e)throw new Error("AsciiArt component requires either sprite, content prop, or children");let y;e instanceof M?y=e.value:y=e;const w=xt(y),{maxW:d,maxH:m}=st(w.frames);r=w.frames,o=w.defaults.loop||!1,h=w.defaults.transparent,a=Math.max(1,d+i),c=Math.max(1,m+i)}const{children:l,content:u,sprite:g,...p}=t;super({...p,width:t.width??t.style?.width??a,height:t.height??t.style?.height??c}),this.frames=[],this.frameIndex=0,this.loop=!1,this.timer=null,this.isLoading=!1,this.isDestroyed=!1,h!==void 0&&(this.spriteTransparentChar=h),this.frames=r,this.loop=o,s&&t.sprite?(this.sprite=t.sprite,this.isLoading=!0,P.getSprite(t.sprite).then(y=>{if(this.isDestroyed)return;this.isLoading=!1,this.loadError=void 0;const{maxW:w,maxH:d}=st(y.frames),m={kind:"sprite",width:w,height:d,data:y};this.updateContentFromAsset(m),A(),this.forceRenderIfNeeded()}).catch(y=>{if(this.isDestroyed)return;this.isLoading=!1,this.loadError=y.message||"Failed to load ASCII art";const w=`Error: ${this.loadError}`;this.frames=[{lines:[[...w]],meta:{duration:0}}];const d=this.border?2:0;this.originalWidth=w.length+d,this.originalHeight=1+d,this.width=w.length+d,this.height=1+d,A(),this.forceRenderIfNeeded()})):(e instanceof M&&(this.contentState=e,this.bind(this.contentState,y=>{this.updateContent(y)})),this.frames.length>1&&this.startAnimation())}startAnimation(){this.maybePlaySound(this.frames[this.frameIndex]?.meta.sound),this.scheduleNext()}scheduleNext(){if(this.isDestroyed)return;const t=this.frames[this.frameIndex],e=Math.max(0,t?.meta.duration??0),i=Number.isFinite(e)&&e>0?e:0;this.clearTimer(),this.timer=setTimeout(()=>{this.advanceFrame()},i)}advanceFrame(){if(!this.isDestroyed&&!(this.frames.length<=1)){if(this.frameIndex++,this.frameIndex>=this.frames.length)if(this.loop)this.frameIndex=0;else{this.frameIndex=this.frames.length-1,this.clearTimer(),A();return}this.maybePlaySound(this.frames[this.frameIndex]?.meta.sound),A(),this.scheduleNext()}}maybePlaySound(t){}clearTimer(){this.timer&&(clearTimeout(this.timer),this.timer=null)}updateContent(t){if(this.isDestroyed)return;this.clearTimer();const e=xt(t),{maxW:i,maxH:s}=st(e.frames),r=i+(this.border?2:0),o=s+(this.border?2:0);this.originalHeight=o,this.originalWidth=r,this.width=r,this.height=o,this.frames=e.frames,this.loop=e.defaults.loop||!1,this.frameIndex=0,this.frames.length>1&&this.startAnimation(),A()}updateContentFromAsset(t){if(this.isDestroyed)return;this.clearTimer();const e=t.data,i=t.width+(this.border?2:0),s=t.height+(this.border?2:0);this.originalHeight=s,this.originalWidth=i,this.width=i,this.height=s,this.frames=e.frames,this.loop=e.defaults.loop||!1,e.defaults.transparent!==void 0&&(this.spriteTransparentChar=e.defaults.transparent),this.frameIndex=0,this.frames.length>1&&this.startAnimation(),A()}extractSpriteName(t){if(t.includes("/sprites/")){const e=t.split("/sprites/");if(e.length>1)return e[1].replace(".art","")}return t}forceRenderIfNeeded(){this.notifyAppOfFocusRefresh()}destroy(){this.isDestroyed=!0,this.clearTimer(),super.destroy()}draw(){try{const t=super.draw();if(!t||t.length===0)return t;const e=this.border?1:0,i=this.border?1:0,s=this.width-(this.border?2:0),r=this.height-(this.border?2:0),o=this.spriteTransparentChar??this.transparentChar,h=this.frames[this.frameIndex];if(!h)return t;const a=h.lines;for(let c=0;c<Math.min(a.length,r);c++){const l=a[c],u=c+i;if(u>=t.length)break;for(let g=0;g<Math.min(l.length,s);g++){const p=g+e;if(!t[u]||p>=t[u].length)break;const y=l[g];y===o?t[u][p]=this.transparentChar:t[u][p]=y}}return this.buffer=t,t}catch(t){return console.error("Art component draw() error:",t),super.draw()}}}function xt(n){const t=[],e=n.replace(/\r\n?/g,`
`).split(`
`);let i={},s=!1,r=!1,o=[];const h=[],a=g=>{h.push({art:o.slice(),meta:g}),o=[]},c=(g,p)=>{try{return JSON.parse(g)}catch(y){return t.push(`JSON parse error ${p}: ${y?.message??String(y)}`),{}}};for(let g=0;g<e.length;g++){const p=e[g],y=p.trimStart();if(!r&&y.length>0&&(r=!0,y.startsWith("§"))){const w=p.slice(p.indexOf("§")+1).trim();if(w){const d=c(w,`in defaults at line ${g+1}`);i={duration:vt(d?.duration),loop:ae(d?.loop),transparent:he(d?.transparent)}}continue}if(y.startsWith("¶")){const w=p.slice(p.indexOf("¶")+1).trim(),d=w?c(w,`in frame meta at line ${g+1}`):{},m={duration:vt(d?.duration),sound:typeof d?.sound=="string"?d.sound:void 0};a(m),s=!0;continue}o.push(p),p.length>0&&(s=!0)}if(o.length&&a({}),!e.some(g=>g.trimStart().startsWith("§")||g.trimStart().startsWith("¶"))&&s){const g={lines:St(e),meta:{duration:0}};return{defaults:{duration:0,loop:!1},frames:[g],errors:t}}const u=h.map(({art:g,meta:p})=>{const y={duration:p?.duration??i.duration??100,sound:p?.sound};return{lines:St(g),meta:y}});return u.length===0&&u.push({lines:[[]],meta:{duration:i.duration??100}}),{defaults:i,frames:u,errors:t}}function St(n){const t=n.slice();return t.length&&t[0]===""&&t.shift(),t.map(i=>[...i])}function st(n){let t=1,e=1;for(const i of n){e=Math.max(e,i.lines.length||1);for(const s of i.lines)t=Math.max(t,s.length||0)}return{maxW:t,maxH:e}}function vt(n){return typeof n=="number"&&Number.isFinite(n)?n:void 0}function ae(n){return typeof n=="boolean"?n:!1}function he(n){return typeof n=="string"&&n.length===1?n:void 0}class Z extends v{constructor(t){const e=t.border?2:0,i=12,s=1+e,{font:r,text:o,letterSpacing:h,...a}=t;super({...a,width:t.width??t.style?.width??i,height:t.height??t.style?.height??s}),this.isLoading=!1,this.letterSpacing=0,this.isDestroyed=!1,this.letterSpacing=h??0,this.font=r,o instanceof M?(this.textState=o,this.text=o.value):this.text=o||"",this.isLoading=!0,P.getFont(r).then(c=>{if(this.isDestroyed)return;this.isLoading=!1,this.loadError=void 0,this.fontAsset=c;const l=this.renderTextWithFont(this.text,c),u=Math.max(...l.map(p=>p.length),0),g=l.length;this.originalHeight=g+e,this.originalWidth=u+e,this.width=u+e,this.height=g+e,this.textState&&this.bind(this.textState,p=>{this.text=p,this.updateFontText()}),A(),this.forceRenderIfNeeded()}).catch(c=>{this.isDestroyed||(this.isLoading=!1,this.loadError=c.message||"Failed to load font",console.error(`Banner: Failed to load font "${this.font}":`,c),A(),this.forceRenderIfNeeded())})}forceRenderIfNeeded(){this.notifyAppOfFocusRefresh()}renderTextWithFont(t,e){if(!t||t.length===0)return[[]];const i=e.height,s=[];for(let r=0;r<i;r++)s.push([]);for(let r=0;r<t.length;r++){const o=t[r],h=e.glyphs.get(o);if(h)for(let a=0;a<i;a++)if(a<h.lines.length){const c=h.lines[a];for(let l=0;l<c.length;l++)s[a].push(c[l]);for(let l=c.length;l<h.width;l++)s[a].push(" ")}else for(let c=0;c<h.width;c++)s[a].push(" ");else for(let a=0;a<i;a++)a===0?s[a].push(o):s[a].push(" ");if(r<t.length-1&&this.letterSpacing>0)for(let a=0;a<i;a++)for(let c=0;c<this.letterSpacing;c++)s[a].push(" ")}return s}updateFontText(){if(this.isDestroyed||!this.fontAsset||!this.text)return;const t=this.renderTextWithFont(this.text,this.fontAsset),e=Math.max(...t.map(r=>r.length),0),i=t.length,s=this.border?2:0;this.originalHeight=i+s,this.originalWidth=e+s,this.width=e+s,this.height=i+s,A()}destroy(){this.isDestroyed=!0,super.destroy()}draw(){try{const t=super.draw();if(!t||t.length===0)return t;const e=this.border?1:0,i=this.border?1:0,s=this.width-(this.border?2:0),r=this.height-(this.border?2:0);if(this.isLoading||this.loadError){const h=[...this.loadError?`Error: ${this.loadError}`:"Loading..."],a=i;if(a<t.length)for(let c=0;c<Math.min(h.length,s);c++){const l=c+e;t[a]&&l<t[a].length&&(t[a][l]=h[c])}return this.buffer=t,t}if(this.fontAsset&&this.text!==void 0){const o=this.renderTextWithFont(this.text,this.fontAsset);for(let h=0;h<Math.min(o.length,r);h++){const a=o[h],c=h+i;if(c>=t.length)break;for(let l=0;l<Math.min(a.length,s);l++){const u=l+e;if(!t[c]||u>=t[c].length)break;const g=a[l];t[c][u]=g}}return this.buffer=t,t}return t}catch(t){return console.error("Banner component draw() error:",t),super.draw()}}}class at extends v{constructor({onClick:t,...e}){let i=e.content||e.label;if(!i&&e.children){const c=Array.isArray(e.children)?e.children:[e.children];c.length>0&&(i=c[0])}const s=i??"Button",r=!1,{children:o,content:h,...a}=e;super({...a,width:e.width??e.style?.width??s.length+7,height:e.height??e.style?.height??4,border:e.border??e.style?.border??!0,label:s,showLabel:r}),this.focusable=!0,this.hasFocus=!1,this.isPressed=!1,this.privateOnClick=t,this.onClick=()=>{this.privateOnClick?.(),this.press()}}press(){this.pressTimer&&clearTimeout(this.pressTimer),this.isPressed=!0,this.pressTimer=setTimeout(()=>{this.isPressed=!1,this.pressTimer=void 0,this.notifyAppOfFocusRefresh(),A()},100)}handleEvent(t){return t==="Enter"||t===" "?(this.press(),this.onClick?.(),!0):!1}draw(){this.buffer=Array.from({length:this.height},()=>Array.from({length:this.width},()=>this.transparentChar));const t=(m,x,S)=>{m>=0&&m<this.width&&x>=0&&x<this.height&&(this.buffer[x][m]=S)},e=this.width-1,i=this.height-1;if(this.isPressed){for(let m=0;m<e;m++)t(m,0," ");for(let m=0;m<i;m++)t(0,m," ")}else{for(let m=1;m<this.width-1;m++)t(m,i,"─");for(let m=1;m<this.height-2;m++)t(e,m,"│");t(e,i-1,"│"),t(1,i,"╰"),t(e,i,"╯"),t(e,1,"╮")}const s=this.isPressed?1:0,r=this.isPressed?1:0;if(this.border){const m=e,x=i;this.focusable&&this.hasFocus,t(s+0,r+0,"╭"),t(s+m-1,r+0,"╮"),t(s+0,r+x-1,"╰"),t(s+m-1,r+x-1,"╯");for(let S=1;S<m-1;S++)t(s+S,r+0,"─"),t(s+S,r+x-1,"─");for(let S=1;S<x-1;S++)t(s+0,r+S,"│"),t(s+m-1,r+S,"│")}const o=this.border?1:0,h=this.border?1:0,a=e-o*2,c=i-h*2,l=this.label||"Button",u=l.length+4,p=s+o+Math.max(Math.floor((a-u)/2),0)+2,y=r+h+Math.floor(c/2);for(let m=0;m<l.length&&p+m<s+e-o;m++)t(p+m,y,l[m]);let w,d;if(this.isPressed?(w="◆",d="◆"):this.hasFocus?(w=">",d="<"):(w=" ",d=" "),(this.hasFocus||this.isPressed)&&(t(s+o,y,w),t(s+e-o-1,y,d)),this.border&&this.hotkey&&this.isHotkeyVisibilityEnabled()){const m=`[${this.hotkey.toUpperCase()}]`;for(let x=0;x<m.length&&x+1<this.width-1;x++)t(s+x+1,r+0,m[x])}return this.buffer}}const et=["╭─╮╭─────","│╭────","╰─│╯","╭│╯","││","││","│","│","│"];function $(n){const t=Math.max(...n.map(i=>i.length)),e=[];for(let i=0;i<t;i++){let s="";for(let r=n.length-1;r>=0;r--){const o=n[r][i]||" ";s+=le(o)}e.push(s)}for(;e.length>0&&e[e.length-1].trim()==="";)e.pop();return e}function le(n){return{"╭":"╮","╮":"╯","╯":"╰","╰":"╭","─":"│","│":"─"," ":" "}[n]||n}const ce=$(et),de=$($(et)),ue=$($($(et))),fe={"top-left":et,"top-right":ce,"bottom-right":de,"bottom-left":ue};class ge extends v{constructor({edge:t,...e}){let i;if(t)i=t;else throw new Error('CelticBorder requires an "edge" property that is one of: top-left, top-right, bottom-left, bottom-right');const s=fe[i],r=Math.max(...s.map(h=>h.length)),o=s.length;super({width:r,height:o,border:!1,...e}),this.lines=s.map(h=>Array.from(h))}draw(){return this.buffer=Array.from({length:this.height},(t,e)=>Array.from({length:this.width},(i,s)=>this.lines[e]?.[s]??" ")),this.buffer}}class pe extends v{constructor(t){super({width:"fill",height:"auto",...t,border:!1,background:void 0}),this.focusable=!1}draw(){if(this.recalculateLayout(),this.children.length===0)return[[" "]];this.buffer=Array.from({length:this.height},()=>Array.from({length:this.width},()=>this.transparentChar));const t=[...this.children].sort((e,i)=>e.z-i.z);for(const e of t){const i=e.draw();for(let s=0;s<i.length;s++)for(let r=0;r<i[s].length;r++){const o=e.x+r,h=e.y+s;if(o>=0&&o<this.width&&h>=0&&h<this.height){const a=i[s][r];a!==e.transparentChar&&(this.buffer[h][o]=a)}}}return this.buffer}}function Et(n,t,e){return t?.children&&(t.children=Array.isArray(t.children)?t.children.flat():[t.children]),typeof n=="function"&&!n.prototype?.constructor?n(t):new n(t)}class ht extends v{constructor(t){if(super(t),this.when=t.when,t.create){const e=t.create,i=t.with||{};this.componentFactory=()=>Et(e,i)}else t.children&&t.children.length>0&&console.error('Case: Using JSX children is not supported. Components will persist in memory and continue running. Use create prop instead: <Case when="'+t.when+'" create={YourComponent} />')}getComponentFactory(){return this.componentFactory}draw(){return[[]]}}class lt extends v{constructor(t){if(super(t),t.create){const e=t.create,i=t.with||{};this.componentFactory=()=>Et(e,i)}else t.children&&t.children.length>0&&console.error("Default: Using JSX children is not supported. Components will persist in memory and continue running. Use create prop instead: <Default create={YourComponent} />")}getComponentFactory(){return this.componentFactory}draw(){return[[]]}}function rt(n){if(typeof n!="function")return;const t=n();if(t instanceof v)return t}class me extends v{constructor(t){if(t.component&&t.condition)throw new Error("Switch cannot have both component and condition props");if(!t.component&&!t.condition)throw new Error("Switch must have either component or condition prop");let e=t,i=[],s;if(t.condition){const r=t.children?Array.isArray(t.children)?t.children:[t.children]:[];i=r.filter(o=>o instanceof ht),s=r.find(o=>o instanceof lt),e={...t,children:void 0}}super(e),this.cases=[],t.condition?(this.cases=i,this.defaultCase=s,this.conditionState=t.condition,this.bind(this.conditionState,()=>this.updateContent())):(this.componentState=t.component,this.bind(this.componentState,()=>this.updateContent())),this.updateContent()}setChildren(t){this.conditionState?(this.cases=t.filter(e=>e instanceof ht),this.defaultCase=t.find(e=>e instanceof lt),this.updateContent()):super.setChildren(t)}updateContent(){const t=[...this.children];for(const e of t)e.destroy(),this.removeChild(e);if(this.conditionState){const e=this.conditionState.value,i=this.cases.find(s=>s.when===e);if(i){const s=i.getComponentFactory();if(s){const r=rt(s);r&&this.addChild(r)}else for(const r of i.getChildren())console.warn('Switch: Case component missing create prop. Components will persist in memory and continue running. Use: <Case when="'+i.when+'" create={'+r.constructor.name+"} />"),this.addChild(r)}else if(this.defaultCase){const s=this.defaultCase.getComponentFactory();if(s){const r=rt(s);r&&this.addChild(r)}else{console.error("Switch: Default component missing create prop. Components will persist in memory and continue running. Use: <Default create={YourComponent} />");for(const r of this.defaultCase.getChildren())this.addChild(r)}}}else if(this.componentState){const e=this.componentState.value;if(e){const i=rt(e);i&&this.addChild(i)}}this.notifyAppOfFocusReset()}}class E extends v{constructor(t){super({...t,width:0,height:0,border:!1,visible:new M(!1)}),this.registrationAttempted=!1,this.destroyed=!1,this.keyBinding=t.keyBinding,this.action=t.action,this.description=t.description,this.priority=t.priority??0,t.disabled instanceof M?(this.disabledState=t.disabled,this.staticDisabled=!1):this.staticDisabled=t.disabled??!1}get disabled(){return this.disabledState?.value??this.staticDisabled}setParent(t){super.setParent(t),this.registerWithApp()}registerWithApp(){if(this.registrationAttempted||this.destroyed)return;let t=this.parent;for(;t&&!t.isApp;)t=t.parent;t&&t.registerKeybind?(t.registerKeybind(this),this.registrationAttempted=!0):this.timeoutId=setTimeout(()=>{this.destroyed||this.registerWithApp()},0)}destroy(){this.destroyed=!0,this.timeoutId!==void 0&&(clearTimeout(this.timeoutId),this.timeoutId=void 0),this.unregisterWithApp(),super.destroy()}unregisterWithApp(){let t=this.parent;for(;t&&!t.isApp;)t=t.parent;t&&t.unregisterKeybind&&t.unregisterKeybind(this)}draw(){return[]}}class ye extends v{constructor(t){super({...t,width:0,height:0,border:!1,visible:new M(!1)}),this.registrationAttempted=!1,this.destroyed=!1,this.priority=t.priority??0,t.enabled instanceof M?(this.disabledState=t.enabled,this.staticDisabled=!1):this.staticDisabled=!(t.enabled??!0),this.actionMap=new Map,t.dpad&&(t.dpad.up&&this.actionMap.set("btn-up",t.dpad.up),t.dpad.down&&this.actionMap.set("btn-down",t.dpad.down),t.dpad.left&&this.actionMap.set("btn-left",t.dpad.left),t.dpad.right&&this.actionMap.set("btn-right",t.dpad.right)),t.buttons&&(t.buttons.a&&this.actionMap.set("btn-a",t.buttons.a),t.buttons.b&&this.actionMap.set("btn-b",t.buttons.b),t.buttons.x&&this.actionMap.set("btn-x",t.buttons.x),t.buttons.y&&this.actionMap.set("btn-y",t.buttons.y)),t.menu&&this.actionMap.set("btn-menu",t.menu)}get disabled(){return this.disabledState?!this.disabledState.value:this.staticDisabled}handleButton(t){const e=this.actionMap.get(t);return e?(e(),!0):!1}setParent(t){super.setParent(t),this.registerWithApp()}registerWithApp(){if(this.registrationAttempted||this.destroyed)return;let t=this.parent;for(;t&&!t.isApp;)t=t.parent;t&&t.registerMobileController?(t.registerMobileController(this),this.registrationAttempted=!0):this.timeoutId=setTimeout(()=>{this.destroyed||this.registerWithApp()},0)}destroy(){this.destroyed=!0,this.timeoutId!==void 0&&(clearTimeout(this.timeoutId),this.timeoutId=void 0),this.unregisterWithApp(),super.destroy()}unregisterWithApp(){let t=this.parent;for(;t&&!t.isApp;)t=t.parent;t&&t.unregisterMobileController&&t.unregisterMobileController(this)}draw(){return[]}}class be extends v{constructor(t){const e=It(),i=Ft();let s=0;s+=15,e&&(s+=14),i&&(s+=17),s+=13,super({...t,width:s,position:t.position??{y:0,x:0},label:"Stats"}),this.showCPU=e,this.showMemory=i}getAsciitoriumRoot(){let t=this.parent;for(;t;){if(t instanceof Tt)return t;t=t.parent}return null}draw(){if(!this.visible)return[];super.draw();const t=this.getAsciitoriumRoot();if(!t)return this.buffer;const e=this.width-(this.border?2:0),i=this.border?1:0,s=this.border?1:0,r=[];if(this.showCPU){const g=t.getCPUUsage().toFixed(1).padStart(5,"0");r.push(`CPU: ${g}% |`)}if(this.showMemory){const g=t.getMemoryUsage().toFixed(1).padStart(6,"0");r.push(`MEM: ${g}mb |`)}const h=t.getRenderTime().toFixed(0).padStart(5,"0");r.push(`FRT: ${h}ms |`);const c=t.getFPS().toString().padStart(3,"0");r.push(`FPS: ${c}`);const l=r.join(" ");for(let u=0;u<l.length&&u<e;u++)i<this.height&&s+u<this.width&&(this.buffer[i][s+u]=l[u]);return this.buffer}}class I extends v{constructor(t={}){const e=t.direction??"horizontal",i=e==="horizontal",s=i?t.width??t.style?.width??t.length??"fill":1,r=i?1:t.height??t.style?.height??t.length??"fill";super({...t,width:s,height:r}),this.direction=e,this.lineChar=t.char??(i?"⎻":"|")}draw(){if(super.draw(),this.direction==="horizontal"){const e=this.border?1:0,i=this.border?this.width-1:this.width;for(let s=e;s<i;s++)this.buffer[0][s]=this.lineChar}else{const e=this.border?1:0,i=this.border?this.height-1:this.height;for(let s=e;s<i;s++)this.buffer[s][0]=this.lineChar}return this.buffer}}class Pt extends v{constructor(t){const{children:e,label:i,...s}=t;super({...s,width:0,height:0,visible:new M(!1)}),this.label=i,this.children=e||[]}draw(){return[[]]}}class we extends v{constructor(t){super({...t,height:t.height??t.style?.height??3,border:t.border??t.style?.border??!0}),this.scrollableViewport=new ft,this.focusedIndex=0,this.selectedIndex=0,this.focusable=!0,this.hasFocus=!1,this.showSelectionBox=t.showSelectionBox??!0,t.children&&t.children.length>0?(this.items=this.flattenChildren(t.children),this.selectedState=t.selected):(this.items=(t.items||[]).map(i=>({label:i,value:i,isGroupHeader:!1,isLastInGroup:!1,groupDepth:0})),this.selectedState=t.selectedItem);const e=Math.max(0,this.items.findIndex(i=>i.value===this.selectedState.value));this.selectedIndex=e,this.focusedIndex=e,this.bind(this.selectedState,i=>{const s=this.items.findIndex(r=>r.value===i);s!==-1&&s!==this.selectedIndex&&(this.selectedIndex=s,this.focusedIndex=s)})}flattenChildren(t){const e=[];for(const i of t)if(i instanceof Pt){e.push({label:i.label,value:null,isGroupHeader:!0,isLastInGroup:!1,groupDepth:0});const s=i.children;s.forEach((r,o)=>{const h=o===s.length-1;e.push({label:r.label,value:r.value,isGroupHeader:!1,isLastInGroup:h,groupDepth:1})})}else e.push({label:i.label,value:i.value,isGroupHeader:!1,isLastInGroup:!1,groupDepth:0});return e}moveNext(){let t=this.focusedIndex+1;for(;t<this.items.length&&this.items[t].isGroupHeader;)t++;t<this.items.length&&(this.focusedIndex=t)}movePrevious(){let t=this.focusedIndex-1;for(;t>=0&&this.items[t].isGroupHeader;)t--;t>=0&&(this.focusedIndex=t)}select(){this.focusedIndex>=0&&this.focusedIndex<this.items.length&&!this.items[this.focusedIndex].isGroupHeader&&(this.selectedIndex=this.focusedIndex,this.selectedState.value=this.items[this.selectedIndex].value)}moveToStart(){this.focusedIndex=0,this.items[0]?.isGroupHeader&&this.moveNext()}moveToEnd(){this.focusedIndex=this.items.length-1,this.items[this.focusedIndex]?.isGroupHeader&&this.movePrevious()}pageDown(){const t=this.height-(this.border?2:0)-(this.height<5?0:1),i=Math.max(1,Math.floor(t/2));let s=Math.min(this.items.length-1,this.focusedIndex+i);for(;s<this.items.length&&this.items[s].isGroupHeader;)s++;s<this.items.length&&(this.focusedIndex=s)}pageUp(){const t=this.height-(this.border?2:0)-(this.height<5?0:1),i=Math.max(1,Math.floor(t/2));let s=Math.max(0,this.focusedIndex-i);for(;s>=0&&this.items[s].isGroupHeader;)s--;s>=0&&(this.focusedIndex=s)}draw(){const t=super.draw(),e=this.border?1:0,i=this.height<5?0:1,s=2,r=this.height-2*e-i,o=Math.max(1,Math.floor(r/s)),h=this.items.map(u=>u.label),a=this.scrollableViewport.calculateScrollWindow({items:h,totalCount:this.items.length,maxVisible:o,focusedIndex:this.focusedIndex}),{startIdx:c,visibleItems:l}=a;return l.forEach((u,g)=>{const p=e+i+g*s,y=e;if(p>=t.length){console.error(`❌ BUFFER OVERFLOW: Trying to draw at y=${p} but buffer.length=${t.length}!`);return}const w=c+g,d=this.items[w],m=w===this.focusedIndex,x=w===this.selectedIndex;let S="",C=y+2;if(d.isGroupHeader?S="":d.groupDepth>0&&(S=d.isLastInGroup?"└─ ":"├─ ",C=y+2),g>0&&d.groupDepth>0&&!d.isGroupHeader){const k=c+g-1;if(k>=0&&k<this.items.length){const q=this.items[k];if(q.groupDepth>0&&!q.isLastInGroup){const Ut=p-1;t[Ut][C]="│"}}}m&&this.hasFocus&&(t[p][y]=">");for(let k=0;k<S.length;k++)t[p][C+k]=S[k];const D=C+S.length,L=Array.isArray(u)?u[0]:u;for(let k=0;k<L.length;k++)t[p][D+k]=L[k];if(x&&!d.isGroupHeader&&this.showSelectionBox){t[p-1][y+1]="╭",t[p][y+1]="│",t[p+1][y+1]="╰";for(let k=2;k<this.width-4;k++)t[p-1][y+k]="─",t[p+1][y+k]="─";t[p-1][this.width-3]="╮",t[p][this.width-3]="│",t[p+1][this.width-3]="╯"}}),this.scrollableViewport.drawScrollIndicators(t,this.width,this.height,e,a.showUpArrow,a.showDownArrow),this.buffer=t,t}}class xe extends v{constructor(t){const{children:e,...i}=t;super({...i,width:0,height:0,visible:new M(!1)}),this.value=t.value,this.label=e||String(t.value)}draw(){return[[]]}}function Se(n,t,e,i,s){if(!n)return{x:0,y:0};const r=t-i,o=e-s;let h=0,a=0;switch(n){case"top-left":h=0,a=0;break;case"top":h=Math.floor(r/2),a=0;break;case"top-right":h=r,a=0;break;case"left":h=0,a=Math.floor(o/2);break;case"center":h=Math.floor(r/2),a=Math.floor(o/2);break;case"right":h=r,a=Math.floor(o/2);break;case"bottom-left":h=0,a=o;break;case"bottom":h=Math.floor(r/2),a=o;break;case"bottom-right":h=r,a=o;break}return{x:h,y:a}}class b extends v{constructor(t){let e=t.content;if(!e&&t.children){const g=Array.isArray(t.children)?t.children:[t.children];g.length>0&&(g.length===1?e=g[0]:e=g)}e||(e="");const{children:i,content:s,scrollable:r,wrap:o,textAlign:h,typewriter:a,typewriterSpeed:c,typewriterPauseFactor:l,...u}=t;super({...u,width:t.width,height:t.height}),this.scrollableViewport=new ft,this.totalLines=[],this.scrollOffset=0,this.isScrollable=!1,this.shouldWrap=!0,this.stateUnsubscribers=[],this.isTypewriter=!1,this.typewriterSpeed=20,this.typewriterPauseFactor=10,this.visibleCharCount=0,this.fullContent="",this.isPaused=!1,this.focusable=!1,this.hasFocus=!1,this.source=e,this.textAlign=h,this.isScrollable=r??!1,this.shouldWrap=o??!0,this.focusable=this.isScrollable,this.isTypewriter=a??!1,this.typewriterSpeed=c??20,this.typewriterPauseFactor=l??10,this.subscribeToStates(),this.isTypewriter&&this.startTypewriter()}startTypewriter(){this.visibleCharCount=0,this.isPaused=!1;const t=1e3/this.typewriterSpeed;this.typewriterIntervalId=globalThis.setInterval(()=>{if(this.isPaused)return;const e=this.fullContent.length;if(this.visibleCharCount<e){this.visibleCharCount++,A();const i=this.fullContent[this.visibleCharCount-1],s=this.visibleCharCount<e?this.fullContent[this.visibleCharCount]:null;i==="."&&(s===" "||s===null)&&(this.isPaused=!0,this.pauseTimeoutId=globalThis.setTimeout(()=>{this.isPaused=!1,this.pauseTimeoutId=void 0},t*this.typewriterPauseFactor))}else this.stopTypewriter()},t)}stopTypewriter(){this.typewriterIntervalId!==void 0&&(globalThis.clearInterval(this.typewriterIntervalId),this.typewriterIntervalId=void 0),this.pauseTimeoutId!==void 0&&(globalThis.clearTimeout(this.pauseTimeoutId),this.pauseTimeoutId=void 0),this.isPaused=!1}subscribeToStates(){const t=[];if(Array.isArray(this.source))for(const e of this.source)H(e)&&t.push(e);else H(this.source)&&t.push(this.source);for(const e of t){const i=()=>{this.isTypewriter&&(this.stopTypewriter(),this.startTypewriter()),A()};e.subscribe(i),this.stateUnsubscribers.push(()=>e.unsubscribe(i))}}destroy(){this.stopTypewriter();for(const t of this.stateUnsubscribers)t();this.stateUnsubscribers=[],super.destroy()}handleEvent(t){if(!this.isScrollable||this.totalLines.length===0)return!1;const e=this.height-(this.border?2:0),i=Math.max(1,e);if(this.totalLines.length<=i)return!1;const s=this.scrollOffset,r=this.scrollableViewport.handleScrollEvent(t,this.scrollOffset,this.totalLines.length);return this.scrollOffset=r,this.scrollOffset!==s}resolveSize(t){if(super.resolveSize(t),this.getOriginalWidth()===void 0){const e=this.getContentAsString().length,i=this.border?2:0;let s=0,r=0;this.gap&&(Array.isArray(this.gap)?this.gap.length===4?(s=this.gap[3],r=this.gap[1]):this.gap.length===2&&(s=r=this.gap[1]):typeof this.gap=="object"?(s=this.gap.left||0,r=this.gap.right||0):typeof this.gap=="number"&&(s=r=this.gap));const o=s+r,h=e+i,a=Math.max(1,t.availableWidth-o);this.width=Math.max(1,Math.min(h,a))}if(this.getOriginalHeight()===void 0){const e=this.width-(this.border?2:0);if(e>0){const i=this.wrapText(this.getContentAsString(),e,1/0);this.isScrollable&&(this.totalLines=i);const s=this.border?2:0;if(!(this.isScrollable&&this.getOriginalHeight()!==void 0)){const r=Math.max(1,i.length+s);this.height=r}}}else if(this.isScrollable){const e=this.width-(this.border?2:0);e>0&&(this.totalLines=this.wrapText(this.getContentAsString(),e,1/0))}}getContentAsString(){let t;if(Array.isArray(this.source))t=this.source.map(e=>{if(typeof e=="string")return e;if(H(e)){const i=e.value;return i==null?"":String(i)}else return String(e)}).join("");else if(H(this.source)){const e=this.source.value;t=e==null?"":String(e)}else t=this.source==null?"":String(this.source);return t=t.replace(/\\¶/g,"\0").replace(/¶/g,`
`).replace(/\u0000/g,"¶"),this.fullContent=t,this.isTypewriter&&this.visibleCharCount<t.length?t.substring(0,this.visibleCharCount):t}draw(){if(!this.visible)return[];super.draw();const t=this.width-(this.border?2:0),e=this.height-(this.border?2:0);let i;if(this.isScrollable&&this.totalLines.length>0){const c=Math.max(1,e);if(this.totalLines.length>c){const l=this.scrollableViewport.calculateScrollWindow({items:this.totalLines,totalCount:this.totalLines.length,maxVisible:c,focusedIndex:this.scrollOffset});i=l.visibleItems;const u=this.border?1:0;this.scrollableViewport.drawScrollIndicators(this.buffer,this.width,this.height,u,l.showUpArrow,l.showDownArrow)}else i=this.totalLines}else i=this.wrapText(this.getContentAsString(),t,e);const s=Math.max(...i.map(c=>c.length),0),{x:r,y:o}=Se(this.textAlign,t,e,s,i.length),h=this.border?r+1:r,a=this.border?o+1:o;for(let c=0;c<i.length&&c<e;c++){const l=i[c],u=a+c;if(u>=this.height)break;const g=l.length;let p=h;this.textAlign&&(this.textAlign.includes("right")?p=h+(s-g):(this.textAlign==="center"||this.textAlign==="top"||this.textAlign==="bottom")&&(p=h+Math.floor((s-g)/2)));for(let y=0;y<l.length&&y<t;y++){const w=p+y;w<this.width&&(this.buffer[u][w]=l[y])}}return this.hasFocus&&this.border&&this.isScrollable&&this.buffer.length>0&&this.buffer[0].length>3&&(this.buffer[0][2]=" ",this.buffer[0][3]=">",this.buffer[0][4]=" "),this.buffer}wrapText(t,e,i){if(e<=0)return[];if(!this.shouldWrap)return t.split(`
`).slice(0,i).map(h=>h.length>e?h.substring(0,e):h);const s=t.split(`
`),r=[];for(const o of s){if(r.length>=i)break;if(o.trim()===""){r.push("");continue}const h=o.split(" ");let a="";for(const c of h){const l=a?`${a} ${c}`:c;if(l.length<=e)a=l;else if(a){if(r.push(a),r.length>=i)break;a=c}else if(c.length>e){let u=c;for(;u.length>e&&r.length<i;)r.push(u.substring(0,e)),u=u.substring(e);u.length>0&&r.length<i&&(a=u)}else a=c;if(r.length>=i)break}a&&r.length<i&&r.push(a)}return r.slice(0,i)}}function Ct(n){return n&&typeof n.value=="number"}class Dt extends v{constructor(t){const e=t.height??t.style?.height??3;super({...t,height:e,border:t.border??t.style?.border??!0}),this.initialHeightCalculated=!1,this.cursorIndex=0,this.suppressCursorSync=!1,this.cursorVisible=!0,this.focusable=!0,this.fixedHeight=typeof e=="number"?e:void 0,this.numericMode=t.numeric===!0||Ct(t.value),this.placeholder=String(t.placeholder??""),this.onEnter=t.onEnter,Ct(t.value)?(this.valueNum=t.value,this.valueStr=new M(String(t.value.value??"")),this.bind(this.valueNum,i=>{this.suppressCursorSync||(this.cursorIndex=String(i??"").length),this.valueStr.value=Number.isFinite(i)?String(i):""})):(this.valueStr=t.value??new M(""),this.valueNum=void 0,this.bind(this.valueStr,i=>{this.suppressCursorSync||(this.cursorIndex=i.length),this.updateHeight()}))}setString(t){if(this.suppressCursorSync=!0,this.valueStr.value=t,this.suppressCursorSync=!1,this.updateHeight(),this.valueNum){const e=Number(t.trim());if(t.trim()===""||Number.isNaN(e))return;this.valueNum.value=e}}updateHeight(){if(this.fixedHeight!==void 0)return;const t=this.calculateRequiredHeight();t!==this.height&&(this.height=t,this.invalidateLayout(),A())}calculateRequiredHeight(){const t=this.border?2:0,e=this.width-t,s=Math.max(1,e-2),r=this.valueStr.value.length>0?this.valueStr.value:this.placeholder,o=this.wrapText(r,s),a=Math.max(1,o.length)+t;return Math.max(3,a)}wrapText(t,e){if(e<=0)return[t];const i=[];let s="";for(let r=0;r<t.length;r++){const o=t[r];o===`
`?(i.push(s),s=""):s.length>=e?(i.push(s),s=o):s+=o}return(s.length>0||i.length===0)&&i.push(s),i}allowChar(t,e){return!!(!this.numericMode||/[0-9]/.test(t)||t==="."&&!e.includes(".")||t==="-"&&this.cursorIndex===0&&!e.startsWith("-"))}startCursorBlink(){this.blinkInterval||(this.cursorVisible=!0,this.blinkInterval=setInterval(()=>{this.cursorVisible=!this.cursorVisible,A()},500))}stopCursorBlink(){this.blinkInterval&&(clearInterval(this.blinkInterval),this.blinkInterval=void 0),this.cursorVisible=!0}resetCursorBlink(){this.stopCursorBlink(),this.startCursorBlink()}destroy(){this.stopCursorBlink(),super.destroy()}handleEvent(t){this.hasFocus&&!this.captureModeActive&&t!=="Escape"&&(this.captureModeActive=!0),this.hasFocus&&this.resetCursorBlink();let e=!1;const i=this.valueStr.value;if(t==="Escape")return this.captureModeActive=!1,!0;if(t.length===1&&t>=" "){if(!this.allowChar(t,i))return!1;const s=i.slice(0,this.cursorIndex),r=i.slice(this.cursorIndex);this.setString(s+t+r),this.cursorIndex++,e=!0}else if(t==="Backspace"){if(this.cursorIndex>0){const s=i.slice(0,this.cursorIndex-1),r=i.slice(this.cursorIndex);this.setString(s+r),this.cursorIndex--,e=!0}}else if(t==="Delete"){if(this.cursorIndex<i.length){const s=i.slice(0,this.cursorIndex),r=i.slice(this.cursorIndex+1);this.setString(s+r),e=!0}}else if(t==="ArrowLeft")this.cursorIndex=Math.max(0,this.cursorIndex-1),e=!0;else if(t==="ArrowRight")this.cursorIndex=Math.min(i.length,this.cursorIndex+1),e=!0;else if(t==="Home")this.cursorIndex=0,e=!0;else if(t==="End")this.cursorIndex=i.length,e=!0;else if(t==="Enter"){if(this.valueNum){const s=Number(this.valueStr.value.trim());Number.isFinite(s)&&(this.valueNum.value=s)}this.onEnter&&this.onEnter(),this.captureModeActive=!1,e=!0}return e}draw(){this.initialHeightCalculated||(this.updateHeight(),this.initialHeightCalculated=!0),this.hasFocus&&!this.blinkInterval?this.startCursorBlink():!this.hasFocus&&this.blinkInterval&&this.stopCursorBlink();const t=super.draw(),e=(this.hasFocus," > "),i=e.length,s=this.border?1:0,r=this.border?1:0,o=this.width-(this.border?2:0),h=Math.max(0,o-i),a=this.valueStr.value.length>0?this.valueStr.value:this.placeholder,c=this.wrapText(a,h);for(let l=0;l<c.length;l++){const u=s+l;if(u>=this.height-(this.border?1:0))break;const g=c[l];if(l===0)for(let y=0;y<i&&y<o;y++)u<t.length&&r+y<t[u].length&&(t[u][r+y]=e[y]);const p=l===0?r+i:r;for(let y=0;y<g.length;y++){const w=p+y;u<t.length&&w<t[u].length&&(t[u][w]=g[y])}}if(this.hasFocus&&this.cursorVisible&&h>0){const{line:l,pos:u}=this.getCursorPosition(c),g=s+l,p=(l===0?r+i:r)+u;g<t.length&&p<t[g].length&&(t[g][p]="▉")}return this.buffer=t,t}getCursorPosition(t){let e=0;for(let s=0;s<t.length;s++){const r=t[s].length;if(this.cursorIndex<=e+r)return{line:s,pos:Math.min(this.cursorIndex-e,r)};e+=r}const i=t.length-1;return{line:Math.max(0,i),pos:t[i]?.length||0}}}class J extends v{constructor(t,e,i){super({...t,width:t.width??t.style?.width??e,height:t.height??t.style?.height??i,border:t.border??t.style?.border??!1}),this.focusable=!0,this.hasFocus=!1,this.valueState=t.value,this.min=t.min??0,this.max=t.max??100,this.step=t.step??1,this.focusable=!t.readonly,this.bind(this.valueState,()=>{})}clampValue(t){return Math.max(this.min,Math.min(this.max,t))}incrementValue(){const t=this.clampValue(this.valueState.value+this.step);this.valueState.value=t}decrementValue(){const t=this.clampValue(this.valueState.value-this.step);this.valueState.value=t}initializeBuffer(){this.buffer=Array.from({length:this.height},()=>Array.from({length:this.width},()=>this.transparentChar))}drawChar(t,e,i){t>=0&&t<this.width&&e>=0&&e<this.height&&(this.buffer[e][t]=i)}calculateNormalizedValue(){const t=this.max-this.min;return(this.valueState.value-this.min)/t}}class jt extends J{constructor(t){super(t,"fill",3)}handleEvent(t){const e=this.valueState.value;if(t==="ArrowRight"||t==="d")this.incrementValue();else if(t==="ArrowLeft"||t==="a")this.decrementValue();else return!1;return this.valueState.value!==e}draw(){this.initializeBuffer();const t=this.width-2,e=this.calculateNormalizedValue(),i=Math.round(e*t),s=Math.max(0,t-i);let r="█".repeat(Math.max(0,i))+" ".repeat(Math.max(0,s));this.drawChar(0,0,"╭");for(let o=1;o<this.width-1;o++)this.drawChar(o,0,"─");this.drawChar(this.width-1,0,"╮"),this.drawChar(0,1,"│");for(let o=0;o<r.length&&o<t;o++){const h=this.hasFocus&&o===i-1&&i>0?"▓":r[o];this.drawChar(o+1,1,h)}this.drawChar(this.width-1,1,"│"),this.drawChar(0,2,"╰");for(let o=1;o<this.width-1;o++)this.drawChar(o,2,"─");if(this.drawChar(this.width-1,2,"╯"),this.hotkey&&this.isHotkeyVisibilityEnabled()){const o=`[${this.hotkey.toUpperCase()}]`;for(let h=0;h<o.length&&h+1<this.width-1;h++)this.drawChar(h+1,0,o[h])}return this.buffer}}class Bt extends J{constructor(t){super(t,"fill",1)}handleEvent(t){const e=this.valueState.value;if(t==="ArrowRight"||t==="d")this.incrementValue();else if(t==="ArrowLeft"||t==="a")this.decrementValue();else return!1;return this.valueState.value!==e}draw(){this.initializeBuffer();const t=this.width-2,e=this.calculateNormalizedValue(),i=Math.round(e*(t-1)),s=Math.floor(this.height/2);this.drawChar(0,s,"├");for(let r=1;r<this.width-1;r++)r-1===i?this.drawChar(r,s,this.hasFocus?"◆":"◇"):this.drawChar(r,s,"─");if(this.drawChar(this.width-1,s,"┤"),this.hotkey&&this.isHotkeyVisibilityEnabled()){const r=`[${this.hotkey.toUpperCase()}]`;for(let o=0;o<r.length&&o+1<this.width-1;o++)this.drawChar(o,0,r[o])}return this.buffer}}class Lt extends J{constructor(t){super(t,"fill",1)}handleEvent(t){const e=this.valueState.value;if(t==="ArrowRight"||t==="d")this.incrementValue();else if(t==="ArrowLeft"||t==="a")this.decrementValue();else return!1;return this.valueState.value!==e}draw(){this.initializeBuffer();const t=Math.floor(this.width/2),e=this.calculateNormalizedValue(),i=Math.round(e*t),s=Math.floor(this.height/2);for(let r=0;r<t;r++){const o=r*2;r<i?this.drawChar(o,s,(this.hasFocus,"◆")):this.drawChar(o,s,"◇")}if(this.hotkey&&this.isHotkeyVisibilityEnabled()){const r=`[${this.hotkey.toUpperCase()}]`;for(let o=0;o<r.length&&o+1<this.width-1;o++)this.drawChar(o,0,r[o])}return this.buffer}}class Wt extends J{constructor(t){super(t,"fill",1)}handleEvent(t){const e=this.valueState.value;if(t==="ArrowRight"||t==="d")this.incrementValue();else if(t==="ArrowLeft"||t==="a")this.decrementValue();else return!1;return this.valueState.value!==e}draw(){this.initializeBuffer();const t=this.calculateNormalizedValue(),e=Math.round(t*this.width),i=Math.floor(this.height/2);for(let s=0;s<e;s++)this.drawChar(s,i,"█");for(let s=e;s<this.width;s++)this.drawChar(s,i,"▒");if(this.hotkey&&this.isHotkeyVisibilityEnabled()){const s=`[${this.hotkey.toUpperCase()}]`;for(let r=0;r<s.length&&r<this.width;r++)this.drawChar(r,0,s[r])}return this.buffer}}class ve extends J{constructor(t){super(t,3,"fill")}handleEvent(t){const e=this.valueState.value;if(t==="ArrowUp")this.incrementValue();else if(t==="ArrowDown")this.decrementValue();else return!1;return this.valueState.value!==e}draw(){this.initializeBuffer();const t=this.height-2,e=this.calculateNormalizedValue(),i=Math.round(e*t);this.drawChar(0,0,"┌"),this.drawChar(1,0,"─"),this.drawChar(2,0,"┐");for(let s=1;s<this.height-1;s++)this.drawChar(0,s,"│"),this.drawChar(2,s,"│"),t-(s-1)<=i?this.drawChar(1,s,this.hasFocus?"█":"▓"):this.drawChar(1,s,"░");if(this.drawChar(0,this.height-1,"└"),this.drawChar(1,this.height-1,"─"),this.drawChar(2,this.height-1,"┘"),this.hotkey&&this.isHotkeyVisibilityEnabled()){const s=`[${this.hotkey.toUpperCase()}]`;for(let r=0;r<s.length&&r<this.width;r++)this.drawChar(r,0,s[r])}return this.buffer}}class N extends v{constructor(t={}){super({...t,width:t.width??t.style?.width??"fill",layout:"row",layoutOptions:{}})}}class F extends v{constructor(t={}){super({...t,width:t.width,height:t.height,layout:"column",layoutOptions:{}})}}class Ht extends v{constructor(t){const{mapAsset:e,player:i,fogOfWar:s,exploredTiles:r,fogCharacter:o,style:h,...a}=t;super({...a,width:t.width??t.style?.width??"fill",height:t.height??t.style?.height??"fill",border:t.border??t.style?.border??!0}),this.focusable=!0,this.mapAssetState=e,this.playerState=i,this.fogOfWarSource=s??!1,this.exploredTilesSource=r,this.fogCharacter=o??" ",this.playerState.subscribe(()=>{A()}),this.mapAssetState.subscribe(()=>{A()})}get mapAsset(){return this.mapAssetState.value}get mapData(){return this.mapAsset?.mapData??[]}get legend(){return this.mapAsset?.legend??{}}get player(){return this.playerState.value}get exploredTiles(){return this.exploredTilesSource?H(this.exploredTilesSource)?this.exploredTilesSource.value:this.exploredTilesSource:new Set}get fogOfWar(){return H(this.fogOfWarSource)?this.fogOfWarSource.value:this.fogOfWarSource}isPositionVisible(t,e,i,s){const r=Math.abs(t-i),o=Math.abs(e-s);return r<=2&&o<=1}isPositionExplored(t,e){return this.exploredTiles.has(`${t},${e}`)}addExploredPosition(t,e){const i=`${t},${e}`;if(this.exploredTilesSource)if(H(this.exploredTilesSource)){const s=this.exploredTilesSource.value;if(!s.has(i)){const r=new Set(s);r.add(i),this.exploredTilesSource.value=r}}else this.exploredTilesSource.add(i)}getVisiblePositions(t,e){const i=[];for(let s=e-1;s<=e+1;s++)for(let r=t-2;r<=t+2;r++)i.push({x:r,y:s});return i}draw(){super.draw();const t=this.mapData,e=this.player,i=this.legend;if(!t||t.length===0)return this.buffer;const s=this.width-(this.border?2:0),r=this.height-(this.border?2:0),o=this.border?1:0,h=this.border?1:0,a=Math.floor(s/2),c=Math.floor(r/2),l=t.length,u=l>0?t[0].length:0;if(u===0||l===0)return this.buffer;if(this.fogOfWar){const d=this.getVisiblePositions(e.x,e.y);for(const m of d)m.x>=0&&m.x<u&&m.y>=0&&m.y<l&&this.addExploredPosition(m.x,m.y)}const g=Math.max(0,e.y-c),p=Math.min(l,g+r),y=Math.max(0,e.x-a),w=Math.min(u,y+s);for(let d=g;d<p;d++){const m=t[d];if(!m)continue;const x=d-g+h;if(x>=this.height)break;for(let S=y;S<w;S++){const C=m[S];if(C===void 0)continue;const D=S-y+o;if(D>=this.width)break;if(S===e.x&&d===e.y){const L=this.getDirectionChar(e.direction);this.buffer[x][D]=L}else{let L=C;if(i&&i[C]&&i[C].showOnMap===!1&&(L=" "),this.fogOfWar){const k=this.isPositionVisible(S,d,e.x,e.y),q=this.isPositionExplored(S,d);k||q?this.buffer[x][D]=L:this.buffer[x][D]=this.fogCharacter}else this.buffer[x][D]=L}}}return this.hasFocus&&this.border&&(this.buffer[0][0]=">"),this.buffer}getDirectionChar(t){switch(t){case"north":return"↑";case"south":return"↓";case"east":return"→";case"west":return"←";default:return"@"}}handleEvent(t){return!1}}const Ce={"here-left":-1,"here-right":19,"near-center":6,"near-left":-1,"near-right":16,"middle-center":9,"middle-left":-1,"middle-right":14,"far-left":6,"far-center":11,"far-right":14};class Vt{constructor(){}getSpriteKey(t,e){return`${t}-${e}`}async loadMaterial(t){try{return P.getMaterial(t)}catch(e){return console.error(`Failed to load material "${t}":`,e),null}}async compose(t,e,i,s,r=!1){const o=Array(s).fill(null).map(()=>Array(i).fill(" ")),h=[{layer:"far",position:"left"},{layer:"far",position:"right"},{layer:"far",position:"center"},{layer:"middle",position:"left"},{layer:"middle",position:"right"},{layer:"middle",position:"center"},{layer:"near",position:"left"},{layer:"near",position:"right"},{layer:"near",position:"center"},{layer:"here",position:"left"},{layer:"here",position:"right"},{layer:"here",position:"center"}];for(const{layer:a,position:c}of h){const l=t[a][c];if(l===null||l===" ")continue;const u=e[l];if(!u)continue;const g=await this.loadMaterial(u.material);if(!g)continue;this.getSpriteKey(a,c);const p=g.layers.find(w=>w.layer===a&&w.position===c);if(!p)continue;const y=this.calculateSpritePosition(a,c,p.lines,p.x,i,s,g.placement);this.renderSprite(o,p.lines,y.x,y.y,i,s,r)}return o}calculateSpritePosition(t,e,i,s,r,o,h){const a=i.length,c=Math.max(...i.map(g=>g.length));let l,u;if(h==="ground"){l=Math.floor((r-c)/2);const p={here:1,near:7,middle:10,far:11}[t];u=o-p-a}else if(u=Math.floor((o-a)/2),s!==void 0)l=s;else{const g=`${t}-${e}`,p=Ce[g];p!==void 0?l=p:e==="left"?l=0:e==="right"?l=r-c:l=Math.floor((r-c)/2)}return{x:l,y:u}}renderSprite(t,e,i,s,r,o,h=!1){for(let a=0;a<e.length;a++){const c=s+a;if(c>=o||c<0)continue;const l=e[a]||"";for(let u=0;u<l.length;u++){const g=i+u;if(g>=r||g<0)continue;const p=l[u];p!=="‽"&&(h&&p===" "||(t[c][g]=p))}}}}const Ae={north:{here:{left:{dx:-2,dy:0},center:{dx:0,dy:0},right:{dx:2,dy:0}},near:{left:{dx:-2,dy:-1},center:{dx:0,dy:-1},right:{dx:2,dy:-1}},middle:{left:{dx:-2,dy:-2},center:{dx:0,dy:-2},right:{dx:2,dy:-2}},far:{left:{dx:-2,dy:-3},center:{dx:0,dy:-3},right:{dx:2,dy:-3}}},south:{here:{left:{dx:2,dy:0},center:{dx:0,dy:0},right:{dx:-2,dy:0}},near:{left:{dx:2,dy:1},center:{dx:0,dy:1},right:{dx:-2,dy:1}},middle:{left:{dx:2,dy:2},center:{dx:0,dy:2},right:{dx:-2,dy:2}},far:{left:{dx:2,dy:3},center:{dx:0,dy:3},right:{dx:-2,dy:3}}},east:{here:{left:{dx:0,dy:-1},center:{dx:0,dy:0},right:{dx:0,dy:1}},near:{left:{dx:2,dy:-1},center:{dx:2,dy:0},right:{dx:2,dy:1}},middle:{left:{dx:4,dy:-1},center:{dx:4,dy:0},right:{dx:4,dy:1}},far:{left:{dx:6,dy:-1},center:{dx:6,dy:0},right:{dx:6,dy:1}}},west:{here:{left:{dx:0,dy:1},center:{dx:0,dy:0},right:{dx:0,dy:-1}},near:{left:{dx:-2,dy:1},center:{dx:-2,dy:0},right:{dx:-2,dy:-1}},middle:{left:{dx:-4,dy:1},center:{dx:-4,dy:0},right:{dx:-4,dy:-1}},far:{left:{dx:-6,dy:1},center:{dx:-6,dy:0},right:{dx:-6,dy:-1}}}};class Rt extends v{constructor(t){const{mapAsset:e,player:i,transparency:s,style:r,...o}=t;super({...o,width:28,height:28,border:t.border??t.style?.border??!0}),this.focusable=!1,this.cachedView=null,this.mapAssetState=e,this.playerState=i,this.transparency=s??!1,this.compositor=new Vt,this.playerState.subscribe(()=>{this.cachedView=null,A()}),this.mapAssetState.subscribe(()=>{this.cachedView=null,A()})}get mapAsset(){return this.mapAssetState.value}get mapData(){return this.mapAsset?.mapData??[]}get legend(){return this.mapAsset?.legend??{}}get player(){return this.playerState.value}isSolid(t,e){const i=this.mapData,s=this.legend;if(e<0||e>=i.length||t<0||t>=i[e].length)return!0;const r=i[e][t];return s[r]?.solid??!1}getDirectionVector(t){switch(t){case"north":return{dx:0,dy:-1};case"south":return{dx:0,dy:1};case"east":return{dx:1,dy:0};case"west":return{dx:-1,dy:0};default:return{dx:0,dy:-1}}}getLeftDirection(t){switch(t){case"north":return"west";case"west":return"south";case"south":return"east";case"east":return"north";default:return"west"}}getRightDirection(t){switch(t){case"north":return"east";case"east":return"south";case"south":return"west";case"west":return"north";default:return"east"}}castRays(){const t=this.mapData,e=this.player;if(!t||t.length===0)return{here:{left:null,center:null,right:null},near:{left:null,center:null,right:null},middle:{left:null,center:null,right:null},far:{left:null,center:null,right:null}};const i=Ae[e.direction],s={here:{left:null,center:null,right:null},near:{left:null,center:null,right:null},middle:{left:null,center:null,right:null},far:{left:null,center:null,right:null}};for(const r of["here","near","middle","far"])for(const o of["left","center","right"]){const h=i[r][o],a=e.x+h.dx,c=e.y+h.dy;c>=0&&c<t.length&&a>=0&&a<t[c].length?s[r][o]=t[c][a]:s[r][o]="█"}return s}draw(){super.draw();const t=this.mapData,e=this.legend;if(!t||t.length===0)return this.buffer;const i=this.width-(this.border?2:0),s=this.height-(this.border?2:0),r=this.border?1:0,o=this.border?1:0,h=this.castRays();if(this.cachedView){const a=this.cachedView;for(let c=0;c<a.length&&c<s;c++)for(let l=0;l<a[c].length&&l<i;l++){const u=a[c][l];u&&u!==" "&&(this.buffer[c+o][l+r]=u)}}else this.compositor.compose(h,e,i,s,this.transparency).then(a=>{this.cachedView=a,A()}).catch(a=>{console.error("Failed to compose first-person view:",a)});return this.buffer}}const B={width:"fill",height:"fill",align:"top-center",border:!0},Me=()=>{const n=new M("Type something here!");return new tt(0,"demo-persistent-counter"),f.jsxs(F,{style:B,label:"State Basics",children:[f.jsx(b,{width:"90%",gap:{top:1},children:"State (In-Memory Reactivity)"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4,bottom:1},children:"State provides reactive variables that trigger re-renders when changed. Use for temporary values that reset on page reload."}),f.jsxs(F,{width:"90%",align:"center",gap:{bottom:1},children:[f.jsx(Dt,{width:"90%",hotkey:"i",value:n}),f.jsxs(b,{gap:{top:1,bottom:1},children:["You typed: ",n]})]}),f.jsx(b,{width:"90%",children:"State API"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:6},children:"• new State<T>(initialValue) — Create reactive state ¶ • state.value — Get or set the current values ¶"}),f.jsx(b,{width:"90%",children:"PersistentState (localStorage Backed)"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4,bottom:1},children:"Asciitorium also supports PersistentState in the browser which extends State with automatic localStorage persistence. Values survive page reloads."}),f.jsx(b,{width:"90%",children:"Tip: You can also use subscribe() for custom side effects when state changes."})]})},ke=()=>f.jsxs(F,{style:B,label:"Component Basics",children:[f.jsx(b,{width:"90%",gap:{top:1},children:"Common Component Properties"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",children:"All components extend the base Component class. It provides a bunch of stuff:"}),f.jsx(b,{width:"90%",gap:{top:1,left:6},children:"• width — number | percentage ¶ • height — number | percentage ¶ • border — default is false ¶ • label — title for the component ¶ • align — align component's children ¶ • gap — Spacing within the component ¶ • background — character to use as fill ¶ • hotkey — keyboard shortcut to focus component ¶ • visible — component visibility ¶"}),f.jsx(v,{gap:1,width:32,height:5,label:"Component",align:"center",border:!0})]}),Ie=()=>f.jsxs(F,{style:B,label:"Text Component Basics",children:[f.jsx(b,{width:"90%",gap:{top:1},children:"Common Properties"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:6},typewriter:!0,children:"• textAlign — Position text within component (9-point grid) ¶ • wrap — Enable/disable text wrapping (default: true) ¶ • scrollable — Enable scrolling for long content (default: false) ¶ • typewriter — Enable typewriter effect (default: false) ¶ • typewriterSpeed — Speed of typewriter effect (chars per second) ¶ • typewriterPauseFactor — Pause multiplier after periods (default: 10) ¶ • \\¶ — use \\¶ to insert a line break within text ¶"}),f.jsx(b,{width:"90%",children:"Text Alignment"}),f.jsx(I,{width:"90%"}),f.jsxs(N,{width:15,align:"center",children:[f.jsx(b,{height:5,width:5,border:!0,textAlign:"top-left",children:"A"}),f.jsx(b,{width:5,height:5,border:!0,textAlign:"top",children:"B"}),f.jsx(b,{width:5,height:5,border:!0,textAlign:"top-right",children:"C"})]}),f.jsxs(N,{width:15,align:"center",children:[f.jsx(b,{height:5,width:5,border:!0,textAlign:"left",children:"D"}),f.jsx(b,{width:5,height:5,border:!0,textAlign:"center",children:"E"}),f.jsx(b,{width:5,height:5,border:!0,textAlign:"right",children:"F"})]}),f.jsxs(N,{width:15,align:"center",children:[f.jsx(b,{height:5,width:5,border:!0,textAlign:"bottom-left",children:"G"}),f.jsx(b,{width:5,height:5,border:!0,textAlign:"bottom",children:"H"}),f.jsx(b,{width:5,height:5,border:!0,textAlign:"bottom-right",children:"I"})]})]}),Fe=()=>{const n=new M("Press X on the keyboard"),t=r=>{n.value=r,setTimeout(()=>{n.value="Press X on the keyboard"},2e3)},e=()=>{const r=Math.floor(Math.random()*60)+10,o=Math.floor(Math.random()*20)+5,h=new pt({sprite:"firework",position:{x:r,y:o}});s.addChild(h),setTimeout(()=>{s.removeChild(h)},800)},i=()=>{t("X pressed - Firework launched!"),e()},s=f.jsxs(F,{style:B,label:"Keybindings & Mobile Basics",children:[f.jsx(b,{width:"90%",gap:{top:1},children:"Keybindings"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4,bottom:1},children:"The Keybind component creates global single key shortcuts that execute custom logic anywhere in your app."}),f.jsx(E,{keyBinding:"x",action:i}),f.jsx(F,{width:"90%",align:"center",border:!0,gap:{bottom:1},children:f.jsx(b,{align:"center",textAlign:"center",width:"80%",gap:{top:1,bottom:1},children:n})}),f.jsx(b,{width:"90%",children:"Keybind Properties"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:6},children:'• keyBinding — Key to bind (e.g., "F12", "j", "Escape") ¶ • action — Function to execute when key is pressed ¶ • description — Optional description for documentation ¶ • disabled — Disable keybinding (supports State for reactive control) ¶'}),f.jsx(b,{width:"90%",children:"Tip: Keybindings are always global and work as long as the Keybind component is part of the app tree and not disabled. They automatically deactivate when their parent component is removed from the tree or when visibility is turned off."})]});return s},Te=()=>{const n=new M(5);return f.jsxs(F,{style:B,label:"Navigation Basics",children:[f.jsx(b,{width:"90%",gap:{top:1},children:"Navigation Keys"}),f.jsx(I,{width:"90%"}),f.jsxs(b,{width:"90%",gap:{bottom:1},children:["Asciitorium uses [Tab] + [Tab+Shift] to move between components. Focused components use a '",">","' to indicate they have focus."]}),f.jsx(b,{width:"90%",gap:{left:6},children:"• Tab — focus on next focusable component ¶ • Shift+Tab — focus on previous focusable component ¶ • Space — press button ¶"}),f.jsx(b,{width:"90%",gap:{bottom:1},children:"Try navigating between these two buttons using [Tab], [Shift+Tab]"}),f.jsxs(F,{width:"90%",align:"center",gap:{bottom:1},children:[f.jsx(jt,{width:50,align:"center",gap:{bottom:1},value:n,min:0,max:20,readonly:!0}),f.jsx(Wt,{width:50,align:"center",gap:{bottom:1},value:n,min:0,max:20,readonly:!0}),f.jsx(Bt,{width:50,align:"center",gap:{bottom:1},value:n,min:0,max:20,readonly:!0}),f.jsx(Lt,{width:50,align:"center",gap:{bottom:1},value:n,min:0,max:20,readonly:!0})]}),f.jsxs(N,{width:"fill",align:"center",gap:{bottom:2},children:[f.jsx(at,{height:6,hotkey:"a",onClick:()=>{n.value>0&&n.value--},children:"Decrement"}),f.jsx(at,{height:6,hotkey:"s",onClick:()=>{n.value<20&&n.value++},children:"Increment"})]})]})},Ee=()=>f.jsxs(F,{style:B,label:"Sprites Basics",children:[f.jsx(b,{width:"90%",gap:{top:1},children:"Sprites"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:2},children:"Sprites are stored in the art/sprites/ directory and can be loaded using the Art component or referenced in map legends via the asset property."}),f.jsx(pt,{sprite:"beating-heart"}),f.jsx(b,{width:"90%",children:"Using Sprites"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4},children:"Display sprites using the Art component:"}),f.jsx(b,{width:56,textAlign:"center",border:!0,align:"center",gap:{left:4,top:1,bottom:1},children:'<Art sprite="beating-heart" />'}),f.jsx(b,{width:"90%",children:"Tip: To learn how to create your own sprites, check out the documentation in the public/art/sprites directory."})]}),Pe=()=>f.jsxs(F,{style:B,label:"Banners",children:[f.jsx(Z,{font:"pencil",text:"pencil",letterSpacing:0,align:"center"}),f.jsx(Z,{font:"marker",text:"marker",letterSpacing:1,align:"center"}),f.jsx(Z,{font:"pixel",text:"pixel",letterSpacing:1,align:"center"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4},children:"Display sprites using the Art component:"}),f.jsx(b,{width:56,textAlign:"center",border:!0,align:"center",gap:{left:4,top:1,bottom:1},children:'<Banner font="marker" text="marker" />'}),f.jsx(b,{width:"90%",gap:{bottom:2},children:"TIP: Fonts for banners are stored in the public/art/fonts/ directory with the .art extension."})]}),mt=new M(null),Ot=new M({x:15,y:5,direction:"east"});P.getMap("example").then(n=>{mt.value=n}).catch(n=>{console.error("Failed to load map:",n)});const _=new gt({mapAsset:mt,player:Ot}),De=new M(new Set),je=()=>f.jsxs(F,{style:B,label:"Maps Basics",children:[f.jsx(E,{keyBinding:"w",action:()=>_.moveForward()}),f.jsx(E,{keyBinding:"s",action:()=>_.moveBackward()}),f.jsx(E,{keyBinding:"a",action:()=>_.turnLeft()}),f.jsx(E,{keyBinding:"d",action:()=>_.turnRight()}),f.jsx(b,{width:"90%",gap:{top:1},children:"Maps"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",children:"Maps are ASCII layouts that define game environments, combining visual representation with legend files that specify gameplay properties."}),f.jsxs(F,{width:"90%",align:"center",gap:{top:1,bottom:1},children:[f.jsx(Ht,{mapAsset:mt,player:Ot,fogOfWar:!1,exploredTiles:De,width:"fill",height:15}),f.jsx(b,{children:"[W] forward • [S] backward • [A] turn left • [D] turn right"})]}),f.jsx(b,{gap:{left:4,top:1,bottom:2},children:"TIP: Use the map-builder.js script to quickly generate maze-like dungeon layouts that you can customize. Maps are stored in art/maps/ with each map in its own directory containing map.art and legend.json files."})]}),yt=new M(null),zt=new M({x:2,y:1,direction:"east"});P.getMap("example").then(n=>{yt.value=n}).catch(n=>{console.error("Failed to load map:",n)});const Q=new gt({mapAsset:yt,player:zt}),Be=()=>f.jsxs(F,{style:B,label:"First Person View Basics",children:[f.jsx(E,{keyBinding:"w",action:()=>Q.moveForward()}),f.jsx(E,{keyBinding:"s",action:()=>Q.moveBackward()}),f.jsx(E,{keyBinding:"a",action:()=>Q.turnLeft()}),f.jsx(E,{keyBinding:"d",action:()=>Q.turnRight()}),f.jsx(b,{width:"90%",gap:{bottom:2,top:1},children:"First Person View (FPV) provides immersive ASCII-based 3D perspective rendering for dungeon crawlers and exploration games."}),f.jsx(b,{width:"90%",children:"Interactive First Person Example"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{bottom:1},children:"Controls: [W] forward • [S] backward • [A] turn left • [D] turn right"}),f.jsx(Rt,{align:"center",gap:{bottom:2},mapAsset:yt,player:zt}),f.jsx(b,{width:"90%",children:"Raycasting System"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4},children:"FirstPersonView uses raycasting with predefined offset cubes to determine what the player can see in each direction:"}),f.jsx(b,{width:"90%",gap:{left:6},children:"• Casts rays at multiple depths (here, near, middle, far) ¶ • Uses GameWorld.isSolid() for wall detection ¶ • Composites materials based on distance and position"}),f.jsx(b,{width:"90%",gap:{top:2},children:"Scene Switching"}),f.jsx(I,{width:"90%"}),f.jsx(b,{width:"90%",gap:{left:4,bottom:2},children:"FirstPersonView supports different visual styles through the scene property, allowing you to switch between wireframe, brick-wall, and other material sets for varied aesthetics."})]});exports.App=Tt;exports.Art=pt;exports.AssetManager=P;exports.Banner=Z;exports.BannerBasics=Pe;exports.BarSlider=Wt;exports.Button=at;exports.Case=ht;exports.CelticBorder=ge;exports.Column=F;exports.Component=v;exports.ComponentBasics=ke;exports.Default=lt;exports.DotSlider=Lt;exports.FPVBasics=Be;exports.FirstPersonCompositor=Vt;exports.FirstPersonView=Rt;exports.Fragment=pe;exports.GaugeSlider=Bt;exports.GridMovement=gt;exports.Keybind=E;exports.KeybindingsBasics=Fe;exports.Line=I;exports.MapView=Ht;exports.MapsBasics=je;exports.MobileController=ye;exports.NavigationBasics=Te;exports.Option=xe;exports.OptionGroup=Pt;exports.PerfMonitor=be;exports.PersistentState=tt;exports.ProgressBarSlider=jt;exports.Row=N;exports.ScrollableViewport=ft;exports.Select=we;exports.SoundManager=G;exports.SpritesBasics=Ee;exports.State=M;exports.StateBasics=Me;exports.Switch=me;exports.Text=b;exports.TextBasics=Ie;exports.TextInput=Dt;exports.VerticalSlider=ve;exports.isCPUSupported=It;exports.isMemorySupported=Ft;exports.isMobileDevice=kt;exports.isNodeEnvironment=ct;exports.isState=H;exports.isWebEnvironment=j;exports.loadArt=R;exports.setupKeyboardHandling=dt;exports.start=Qt;exports.validateWebEnvironment=ut;