UNPKG

asciitorium

Version:
4,037 lines 139 kB
import { jsxs as I, jsx as m } from "asciitorium/jsx-runtime";
class z {
  constructor() {
    this.renderCallback = null, this.renderPending = !1;
  }
  static getInstance() {
    return z.instance || (z.instance = new z()), z.instance;
  }
  setRenderCallback(t) {
    this.renderCallback = t;
  }
  requestRender() {
    this.renderPending || (this.renderPending = !0, queueMicrotask(() => this.executeRender()));
  }
  executeRender() {
    this.renderPending = !1, this.renderCallback?.();
  }
}
const yt = z.getInstance();
function bt(o) {
  yt.setRenderCallback(o);
}
function A() {
  yt.requestRender();
}
function R(o) {
  return typeof o == "object" && typeof o.subscribe == "function";
}
function wt() {
  return typeof process < "u" && typeof process.cpuUsage == "function" && typeof process.memoryUsage == "function";
}
function V() {
  return typeof window < "u" && typeof document < "u";
}
function Tt() {
  if (!V()) return !1;
  const o = "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 o || t && e;
}
function Et() {
  return wt();
}
function Pt() {
  return wt() || typeof performance < "u" && performance.memory;
}
async function xt(o, t) {
  if (V())
    window.addEventListener("keydown", (e) => {
      e.key === "F1" && e.preventDefault(), e.key === "Tab" && e.shiftKey ? o("Shift+Tab", e) : o(e.key, e);
    }), Tt() && t && (Dt(t), Lt(o));
  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 = Wt(s);
      o(r);
    });
  }
}
function Dt(o) {
  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", (n) => {
      n.preventDefault(), o(s);
    }), r.addEventListener("click", (n) => {
      n.preventDefault(), o(s);
    }));
  });
}
function Lt(o) {
  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 n = r.changedTouches[0].clientX, h = r.changedTouches[0].clientY, a = Date.now(), c = n - e, l = h - i, u = a - s, f = 50;
    u > 500 || Math.abs(c) > Math.abs(l) && Math.abs(c) > f && (c > 0 ? o("Tab") : o("Shift+Tab"));
  });
}
function Wt(o) {
  if (!o) return "";
  switch (o.ctrl && o.name === "c" && process.exit(), o.name) {
    case "return":
      return "Enter";
    case "escape":
      return "Escape";
    case "backspace":
      return "Backspace";
    case "tab":
      return o.shift ? "Shift+Tab" : "Tab";
    case "up":
      return "ArrowUp";
    case "down":
      return "ArrowDown";
    case "left":
      return "ArrowLeft";
    case "right":
      return "ArrowRight";
    default:
      return o.sequence || "";
  }
}
function St() {
  if (V() && !document.getElementById("screen"))
    throw new Error("Missing #screen element for DOM renderer");
}
async function Y(o) {
  if (typeof window < "u" && typeof fetch < "u") {
    let t = null, e = null;
    try {
      const i = await fetch(o);
      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/${o}`, 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 ${o} - 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", o);
    try {
      return await t(i, "utf-8");
    } catch {
    }
    const s = e(
      process.cwd(),
      "node_modules",
      "asciitorium",
      "public",
      o
    );
    try {
      return await t(s, "utf-8");
    } catch {
    }
    throw new Error(
      `Failed to load ${o} - not found in project (${i}) or library (${s})`
    );
  }
}
async function de(o) {
  St(), await xt((t) => o.handleKey(t)), bt(() => o.render());
}
function E(o) {
  if (typeof o == "number")
    return { top: o, bottom: o, left: o, right: o };
  if (Array.isArray(o)) {
    if (o.length === 1)
      return { top: o[0], bottom: o[0], left: o[0], right: o[0] };
    if (o.length === 2)
      return { top: o[0], bottom: o[0], left: o[1], right: o[1] };
    if (o.length === 3) {
      const [t, e, i] = o;
      return { top: t, bottom: i, left: e, right: e };
    } else if (o.length >= 4) {
      const [t, e, i, s] = o;
      return { top: t, right: e, bottom: i, left: s };
    } else
      return { top: 0, bottom: 0, left: 0, right: 0 };
  }
  if (typeof o == "object" && o !== null) {
    const t = {
      top: o.top ?? 0,
      bottom: o.bottom ?? 0,
      left: o.left ?? 0,
      right: o.right ?? 0
    };
    return o.y !== void 0 && (t.top = o.y, t.bottom = o.y), o.x !== void 0 && (t.left = o.x, t.right = o.x), o.top !== void 0 && (t.top = o.top), o.bottom !== void 0 && (t.bottom = o.bottom), o.left !== void 0 && (t.left = o.left), o.right !== void 0 && (t.right = o.right), t;
  }
  return { top: 0, bottom: 0, left: 0, right: 0 };
}
function Ht(o) {
  const t = o.match(/^(\d+(?:\.\d+)?)%$/);
  if (!t)
    throw new Error(`Invalid percentage format: ${o}`);
  return parseFloat(t[1]) / 100;
}
function Bt(o) {
  return typeof o == "string" && o.endsWith("%");
}
function lt(o, t, e) {
  if (o !== void 0) {
    if (typeof o == "number")
      return o;
    if (Bt(o)) {
      const i = Ht(o), s = e === "width" ? t.availableWidth : t.availableHeight;
      return Math.floor(s * i);
    }
    if (o !== "auto") {
      if (o === "fill")
        return e === "width" ? t.availableWidth : t.availableHeight;
      throw new Error(`Unknown size value: ${o}`);
    }
  }
}
function O(o, t, e = 0) {
  const i = o - 2 * e, s = t - 2 * e;
  return {
    parentWidth: o,
    parentHeight: t,
    availableWidth: Math.max(0, i),
    availableHeight: Math.max(0, s)
  };
}
class Vt {
  constructor(t) {
  }
  layout(t, e) {
    const i = t.border ? 1 : 0, s = t.height - 2 * i, r = t.width - 2 * i, n = e.filter((d) => !(d.width === 0 || d.height === 0 || d.visible !== void 0 && !d.visible)), h = [];
    let a = 0;
    for (const d of n) {
      if (d.fixed) {
        const S = O(t.width, t.height, i);
        d.resolveSize(S);
        continue;
      }
      const p = E(d.gap);
      if (d.getOriginalWidth() === "fill")
        h.push(d), a += p.left + p.right;
      else {
        const S = O(t.width, t.height, i);
        d.resolveSize(S), a += p.left + d.width + p.right;
      }
    }
    const c = r - a, l = h.length;
    if (l > 0 && c > 0) {
      const d = Math.floor(c / l);
      for (const p of h) {
        const x = E(p.gap);
        p.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 n) {
      if (d.fixed) continue;
      const p = E(d.gap);
      u += p.left + d.width + p.right;
    }
    const f = r - u;
    let g = 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 [p, x] = d.split("-").length === 2 ? d.split("-") : d === "center" ? ["center", "center"] : ["top", "left"];
      x === "center" ? g = Math.floor(f / 2) : x === "right" && (g = f), y = p;
    }
    let w = i + g;
    for (const d of n) {
      if (d.fixed) continue;
      const p = E(d.gap);
      w += p.left;
      const x = s - p.top - p.bottom, S = d.getOriginalHeight();
      if (S === "fill")
        d.height = Math.max(1, x);
      else if (S === void 0) {
        const P = O(t.width, t.height, i);
        d.resolveSize(P);
      }
      let C = 0;
      y === "center" ? C = Math.floor((x - d.height) / 2) : y === "bottom" && (C = x - d.height), d.x = w, d.y = i + p.top + C, w += d.width + p.right;
    }
  }
}
class Rt {
  constructor(t) {
  }
  layout(t, e) {
    const i = t.border ? 1 : 0, s = t.width - 2 * i, r = t.height - 2 * i, n = e.filter((d) => !(d.width === 0 || d.height === 0 || d.visible !== void 0 && !d.visible)), h = [];
    let a = 0;
    for (const d of n) {
      if (d.fixed) {
        const S = O(t.width, t.height, i);
        d.resolveSize(S);
        continue;
      }
      const p = E(d.gap);
      if (d.getOriginalHeight() === "fill")
        h.push(d), a += p.top + p.bottom;
      else {
        const S = O(t.width, t.height, i);
        d.resolveSize(S), a += p.top + d.height + p.bottom;
      }
    }
    const c = r - a, l = h.length;
    if (l > 0 && c > 0) {
      const d = Math.floor(c / l);
      for (const p of h) {
        const x = E(p.gap);
        p.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 n) {
      if (d.fixed) continue;
      const p = E(d.gap);
      u += p.top + d.height + p.bottom;
    }
    const f = r - u;
    let g = 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 [p, x] = d.split("-").length === 2 ? d.split("-") : d === "center" ? ["center", "center"] : ["top", "left"];
      p === "center" ? g = Math.floor(f / 2) : p === "bottom" && (g = f), y = x;
    }
    let w = i + g;
    for (const d of n) {
      if (d.fixed) continue;
      const p = E(d.gap);
      w += p.top;
      const x = s - p.left - p.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 + p.left + C, d.y = w, w += d.height + p.bottom;
    }
  }
}
const X = class X {
  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());
  }
};
X.layouts = /* @__PURE__ */ new Map(), X.layouts.set("row", Vt), X.layouts.set("column", Rt);
let rt = X;
const B = {
  topLeft: "╭",
  topRight: "╮",
  bottomLeft: "╰",
  bottomRight: "╯",
  horizontal: "─",
  vertical: "│"
};
function Ot(o) {
  const t = o.style || {};
  return {
    width: o.width ?? t.width,
    height: o.height ?? t.height,
    border: o.border ?? t.border,
    background: o.background ?? t.background,
    align: o.align ?? t.align,
    position: o.position ?? t.position,
    gap: o.gap ?? t.gap,
    font: t.font,
    layout: t.layout
  };
}
const K = class K {
  /**
   * Initializes a new Component with the provided properties.
   *
   * @param props Configuration object containing style, layout, and behavior options
   */
  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 = Ot(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;
  }
  /**
   * Gets the current visibility state of the component.
   * @returns True if the component is visible (default), false if explicitly hidden
   */
  get visible() {
    return this.visibleState?.value ?? !0;
  }
  /**
   * Initializes child components from props and sets up layout.
   *
   * @param props Component properties containing potential children
   */
  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();
  }
  /**
   * Validates that a potential child is a valid Component.
   *
   * @param child Potential child component to validate
   * @returns True if the child is a valid Component
   */
  isValidChild(t) {
    return t && typeof t == "object" && typeof t.setParent == "function";
  }
  // Child management methods
  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 = rt.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 = K.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 = K.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));
  }
  /**
   * Registers a cleanup function to be called when the component is destroyed.
   * Use this for cleaning up timers, intervals, event listeners, and other resources.
   *
   * Example:
   * ```typescript
   * const intervalId = setInterval(() => {...}, 1000);
   * component.registerCleanup(() => clearInterval(intervalId));
   * ```
   *
   * Note: State subscriptions created via bind() are automatically cleaned up
   * and do not need to be registered with registerCleanup().
   *
   * @param fn Cleanup function to execute on destroy
   */
  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 = [];
  }
  // Auto-sizing methods
  static calculateAutoWidth(t, e) {
    return !t || t.length === 0 ? 1 : e === "row" ? t.reduce((i, s) => {
      const r = E(s.gap);
      return i + s.width + r.left + r.right;
    }, 0) : Math.max(
      ...t.map((i) => {
        const s = E(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 = E(s.gap);
      return i + s.height + r.top + r.bottom;
    }, 0) : Math.max(
      ...t.map((i) => {
        const s = E(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);
  }
  /**
   * Notifies the application's focus manager that the component tree has changed
   * and focus needs to be completely reset (e.g., when swapping out child components).
   */
  notifyAppOfFocusReset() {
    let t = this;
    for (; t && !t.isApp; )
      t = t.parent;
    t && t.focus && t.focus.reset(t);
  }
  // Size resolution methods
  getOriginalWidth() {
    return this.originalWidth;
  }
  getOriginalHeight() {
    return this.originalHeight;
  }
  resolveSize(t) {
    if (this.originalWidth !== void 0) {
      const e = lt(this.originalWidth, t, "width");
      e !== void 0 && (this.width = e);
    }
    if (this.originalHeight !== void 0) {
      const e = lt(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;
  }
  /**
   * Draws the border around the component with diamond corners for focus indication.
   *
   * @param drawChar Helper function for safe character drawing within bounds
   */
  drawBorder(t) {
    const e = this.width, i = this.height;
    t(0, 0, B.topLeft), t(e - 1, 0, B.topRight), t(0, i - 1, B.bottomLeft), t(e - 1, i - 1, B.bottomRight);
    for (let s = 1; s < e - 1; s++)
      t(s, 0, B.horizontal), t(s, i - 1, B.horizontal);
    for (let s = 1; s < i - 1; s++)
      t(0, s, B.vertical), t(e - 1, s, B.vertical);
  }
  /**
   * Renders all child components sorted by z-index and composites them into the buffer.
   */
  renderChildren() {
    const t = [...this.children].sort((e, i) => e.z - i.z);
    for (const e of t)
      this.compositeChildBuffer(e);
  }
  /**
   * Composites a single child component's buffer into this component's buffer.
   *
   * @param child The child component to composite
   */
  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, n = t.y + i;
        if (r >= 0 && r < this.width && n >= 0 && n < this.height) {
          if (!this.buffer[n]) continue;
          const h = e[i][s];
          h !== t.transparentChar && (this.border && (n === 0 || n === this.height - 1 || r === 0 || r === this.width - 1) || r < this.buffer[n].length && (this.buffer[n][r] = h));
        }
      }
  }
  /**
   * Check if hotkey visibility is enabled by finding the App's FocusManager
   */
  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;
  }
};
K.BYPASS_KEYS = ["Tab", "Shift+Tab"];
let v = K;
class k {
  /**
   * Creates a new State instance with an initial value.
   *
   * @param initialValue - The initial value for the state
   */
  constructor(t) {
    this.listeners = [], this._value = t;
  }
  /**
   * Gets the current state value.
   *
   * @returns The current value
   */
  get value() {
    return this._value;
  }
  /**
   * Sets a new state value and notifies all subscribers if the value changed.
   *
   * Only triggers notifications if the new value is different from the current
   * value (using strict equality comparison).
   *
   * @param newValue - The new value to set
   */
  set value(t) {
    this._value !== t && (this._value = t, this.listeners.forEach((e) => e(t)));
  }
  /**
   * Subscribes to state changes.
   *
   * The callback function will be invoked immediately with the current value,
   * and again whenever the state value changes.
   *
   * @param fn - Callback function to invoke on state changes
   *
   * @example
   * ```tsx
   * const state = new State(5);
   * state.subscribe((value) => {
   *   console.log('Value changed to:', value);
   * });
   * ```
   */
  subscribe(t) {
    this.listeners.push(t), t(this._value);
  }
  /**
   * Unsubscribes from state changes.
   *
   * Removes the specified callback function from the list of subscribers.
   * The function will no longer be called when the state changes.
   *
   * @param fn - The callback function to remove
   *
   * @example
   * ```tsx
   * const state = new State(10);
   * const handler = (value) => console.log(value);
   * state.subscribe(handler);
   * state.unsubscribe(handler); // Stop listening
   * ```
   */
  unsubscribe(t) {
    this.listeners = this.listeners.filter((e) => e !== t);
  }
}
const $ = class $ {
  constructor() {
    this.contextStack = [], this.index = 0, this.hotkeyMap = /* @__PURE__ */ new Map(), this.hotkeyVisibilityState = new k(!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));
  }
  // TODO: this doesn't work, still goes forward
  focusPrevious() {
    this.currentContext.length !== 0 && (this.clearFocus(), this.index = (this.index - 1 + this.currentContext.length) % this.currentContext.length, this.setFocus(this.index));
  }
  /**
   * Unified key handling for both hotkeys and navigation
   */
  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);
  }
  /**
   * Build hotkey map from focusable components
   */
  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($.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 $.RESERVED_KEYS.has(t) || $.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;
  }
};
$.RESERVED_KEYS = /* @__PURE__ */ new Set([
  "ArrowUp",
  "ArrowDown",
  "ArrowLeft",
  "ArrowRight",
  // Arrow keys
  "Tab",
  "Enter",
  " ",
  "Escape",
  // Navigation and control
  "Backspace",
  "Delete",
  "Home",
  "End",
  // Text editing
  "PageUp",
  "PageDown"
  // Scrolling
]);
let nt = $;
class zt {
  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 $t {
  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 Ut extends v {
  /**
   * Creates a new App instance.
   *
   * Automatically detects the environment (web vs CLI) and initializes the
   * appropriate renderer. Sets up keyboard handling, focus management, and
   * performance monitoring.
   *
   * @param props - App configuration options
   */
  constructor(t) {
    const e = t.style?.font, i = t.font ?? e, s = Nt(i), r = s.getScreenSize(), n = t.style?.width, h = t.style?.height, a = t.style?.layout, c = typeof (t.width ?? n) == "number", l = typeof (t.height ?? h) == "number", u = {
      ...t,
      width: t.width ?? n ?? 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 = /* @__PURE__ */ new Map(), this.mobileControllerRegistry = [], this.renderer = s, this.focus = new nt(), this.fixedWidth = c, this.fixedHeight = l, this.setupResizeHandling(), this.focus.reset(this), this.focus.hotkeyVisibilityState.subscribe(() => {
      this.render();
    }), this.render(), this.updatePerformanceMetrics();
    const f = setInterval(() => {
      this.currentFPS = this.fpsCounter, this.fpsCounter = 0, this.totalRenderTime = 0, this.updatePerformanceMetrics();
    }, 1e3);
    this.registerCleanup(() => clearInterval(f));
  }
  /**
   * Renders the application to the screen.
   *
   * Traverses the component tree, renders each component to buffers, and
   * composites them based on z-index. Updates performance metrics and
   * increments the FPS counter.
   *
   * This method is called automatically on initialization and whenever
   * components request a re-render via the RenderScheduler.
   */
  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, n) => (r.z ?? 0) - (n.z ?? 0));
    for (const r of i) {
      if (!r.visible) continue;
      const n = r.draw(), h = r.x, a = r.y, c = r.transparentChar;
      for (let l = 0; l < n.length; l++) {
        const u = a + l;
        if (!(u < 0 || u >= this.height))
          for (let f = 0; f < n[l].length; f++) {
            const g = h + f;
            if (g < 0 || g >= this.width) continue;
            const y = n[l][f];
            y !== c && (e[u][g] = 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;
  }
  // Add keybind registration methods
  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);
  }
  // Add mobile controller registration methods
  registerMobileController(t) {
    this.mobileControllerRegistry.push(t);
  }
  unregisterMobileController(t) {
    const e = this.mobileControllerRegistry.indexOf(t);
    e !== -1 && this.mobileControllerRegistry.splice(e, 1);
  }
  // Handle mobile button press
  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() {
    St(), await xt(
      (t, e) => this.handleKey(t, e),
      (t) => this.handleMobileButton(t)
    ), bt(() => 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 = O(
        t.parent.width,
        t.parent.height,
        s
      );
      t.resolveSize(r);
    } else {
      const s = O(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 Nt(o) {
  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 zt(t, o);
  } else
    return new $t();
}
class ot extends k {
  constructor(t, e) {
    const i = ot.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 (V())
      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 (V())
      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;
  }
  // Optional method to clear stored state
  clearStorage() {
    if (V())
      try {
        const t = `asciitorium-${this.storageKey}`;
        localStorage.removeItem(t);
      } catch (t) {
        console.warn("Failed to clear state from localStorage:", t);
      }
  }
}
class vt {
  calculateScrollWindow(t) {
    const { items: e, totalCount: i, maxVisible: s, focusedIndex: r } = t, n = Math.max(
      0,
      Math.min(
        r - Math.floor(s / 2),
        Math.max(0, i - s)
      )
    ), h = e.slice(n, n + s);
    return {
      startIdx: n,
      visibleItems: h,
      showUpArrow: n > 0,
      showDownArrow: n + 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, n) {
    if (r) {
      const h = s, a = e - 2;
      h >= 0 && h < i && a >= 0 && a < e && (t[h][a] = "↑");
    }
    if (n) {
      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;
  }
  // Dimension calculation methods
  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;
  }
  // Private asset loading methods
  static async loadMapAsset(t) {
    try {
      const [e, i] = await Promise.all([
        Y(`art/maps/${t}/map.art`),
        Y(`art/maps/${t}/legend.json`)
      ]), s = e.split(`
`), r = JSON.parse(i), n = this.expandLegendFormat(r);
      return {
        mapData: s,
        legend: n
      };
    } catch (e) {
      const i = e instanceof Error ? e.message : "Unknown error";
      throw new Error(`Failed to load map "${t}": ${i}`);
    }
  }
  /**
   * Expands legend format from array format to character map
   * - Format: { "legend": [ { "chars": [...], ...props } ] }
   */
  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 Y(`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 Y(`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 Y(`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}`);
    }
  }
  // Material parsing (from FirstPersonCompositor logic)
  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 n, h = !1;
    try {
      if (n = JSON.parse(r), n.artType === "fpsScenery" && n.layer)
        h = !0;
      else if (n.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 g = 0; g < l.length; g++)
        if (l[g].trim().startsWith("¶")) {
          u = g;
          break;
        }
      const f = u === -1 ? l : l.slice(0, u);
      if (a.push({
        layer: n.layer,
        position: n.position,
        x: n.x,
        lines: f
      }), u !== -1) {
        const g = l.slice(u).join(`
`).split("¶");
        for (const y of g)
          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: n.usage || (h ? "first-person" : "unknown"),
      placement: n.placement,
      onEnterSound: n.onEnterSound,
      onExitSound: n.onExitSound,
      ambientSound: n.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 n = i.slice(1);
          e.push({
            layer: r.layer,
            position: r.position,
            x: r.x,
            lines: n
          });
        }
      } catch (r) {
        console.warn("Failed to parse layer metadata:", s, r);
      }
  }
  // Sprite parsing using § and ¶ separators
  // Format: § {sprite metadata} followed by frames separated by ¶ {frame metadata}
  // Example:
  //   § {"kind":"sprite","loop":true,"default-frame-rate":120}
  //   frame 1 content...
  //   ¶ {"duration":1000}
  //   frame 2 content...
  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], n = r.split(`
`), h = n[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(
      n[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 f = u.split(`
`);
      if (f.length === 0) continue;
      const g = f[0].trim();
      let y = { duration: i.duration };
      if (g.startsWith("{")) {
        try {
          const d = JSON.parse(g);
          y = {
            duration: d.duration || i.duration,
            ...d.sound && { sound: d.sound }
          };
        } catch (d) {
          console.warn("Failed to parse frame metadata:", g, d);
        }
        const w = f.slice(1);
        e.push({
          lines: this.normalizeBlock(w),
          meta: y
        });
      } else
        e.push({
          lines: this.normalizeBlock(f),
          meta: y
        });
    }
    return { frames: e, defaults: i };
  }
  // Font parsing using § and ¶ separators
  // Format: § {"kind":"font"} followed by glyphs separated by ¶ {"character":"a"}
  static parseFontData(t) {
    const e = /* @__PURE__ */ 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], n = r.split(`
`), h = n[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(
      n[0].length + 1
    ).split("¶");
    for (let l = 1; l < c.length; l++) {
      const u = c[l];
      if (!u.trim()) continue;
      const f = u.split(`
`);
      if (f.length === 0) continue;
      const g = f[0].trim();
      if (g.startsWith("{"))
        try {
          const y = JSON.parse(g);
          if (!y.character) {
            console.warn(
              "Glyph metadata missing character property:",
              g
            );
            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:",
              g
            );
            continue;
          }
          const d = this.normalizeFontBlock(f.slice(1)), p = d.length, x = Math.max(
            ...d.map((C) => C.length),
            0
          ), S = {
            character: w[0],
            // Store first character as primary
            lines: d,
            width: x,
            height: p
          };
          for (const C of w)
            e.set(C, S);
          i = Math.max(i, p);
        } catch (y) {
          console.warn("Failed to parse glyph metadata:", g, y);
        }
    }
    if (e.size === 0)
      throw new Error("No valid glyphs found in font file");
    return {
      glyphs: e,
      height: i
    };
  }
  // Helper method to normalize sprite blocks (from Art component)
  static normalizeBlock(t) {
    const e = t.slice();
    return e.length && e[0] === "" && e.shift(), e.map((s) => [...s]);
  }
  // Helper method to normalize font glyph blocks
  // Unlike normalizeBlock, this preserves ALL lines including leading empty lines
  // to maintain vertical positioning of glyphs
  static normalizeFontBlock(t) {
    return t.map((i) => [...i]);
  }
};
U.mapCache = /* @__PURE__ */ new Map(), U.materialCache = /* @__PURE__ */ new Map(), U.spriteCache = /* @__PURE__ */ new Map(), U.fontCache = /* @__PURE__ */ new Map();
let L = U;
const j = class j {
  static setEnabled(t) {
    this.enabled = t;
  }
  static isEnabled() {
    return this.enabled;
  }
  static async playSound(t, e = !1) {
    if (V() && 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 (!V())
      return;
    const i = this.playingAudio.get(t);
    if (!i)
      return;
    const s = i.volume, r = Date.now(), n = setInterval(() => {
      const h = Date.now() - r, a = Math.min(h / e, 1);
      i.volume = s * (1 - a), a >= 1 && (clearInterval(n), i.pause(), i.currentTime = 0, this.playingAudio.delete(t));
    }, 16);
  }
  static clearCache() {
    this.audioCache.clear();
  }
};
j.audioCache = /* @__PURE__ */ new Map(), j.playingAudio = /* @__PURE__ */ new Map(), j.enabled = !0;
let Z = j;
class Ct {
  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 };
  }
  // Read-only access methods
  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;
  }
  // Legend interpretation methods
  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);
  }
  // Movement methods
  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
    };
  }
  // Private helper methods
  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 };
      // 2 units for east/west to match map aspect ratio
      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"], n = (i.indexOf(t) + (e === "left" ? -1 : 1) + 4) % 4;
    return i[n];
  }
  movePlayerBy(t, e, i) {
    const s = this.getPlayer(), r = this.getMapData(), n = r.length, h = n > 0 ? r[0].length : 0;
    if (h === 0 || n === 0) return !1;
    const a = Math.max(0, Math.min(h - 1, s.x + t)), c = Math.max(0, Math.min(n - 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 L.getMaterial(
          s.material
        );
        r.onEnterSound && Z.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 L.getMaterial(
          s.material
        );
        r.onExitSound && Z.playSound(r.onExitSound);
      } catch (r) {
        console.debug("Could not check exit sound:", r);
      }
  }
}
class At extends v {
  // Track if component has been destroyed
  constructor(t) {
    let e = t.content;
    const i = t.border ? 2 : 0, s = !!t.sprite;
    let r = [], n = !1, h, a, c;
    if (s)
      r = [
        {
          lines: [["L", "o", "a", "d", "i", "n", "g", ".", ".", "."]],
          meta: { duration: 0 }
        }
      ], n = !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 k ? y = e.value : y = e;
      const w = ct(y), { maxW: d, maxH: p } = et(w.frames);
      r = w.frames, n = w.defaults.loop || !1, h = w.defaults.transparent, a = Math.max(1, d + i), c = Math.max(1, p + i);
    }
    const { children: l, content: u, sprite: f, ...g } = t;
    super({
      ...g,
      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 = n, s && t.sprite ? (this.sprite = t.sprite, this.isLoading = !0, L.getSprite(t.sprite).then((y) => {
      if (this.isDestroyed) return;
      this.isLoading = !1, this.loadError = void 0;
      const { maxW: w, maxH: d } = et(y.frames), p = {
        kind: "sprite",
        width: w,
        height: d,
        data: y
      };
      this.updateContentFromAsset(p), 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 k && (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 = ct(t), { maxW: i, maxH: s } = et(e.frames), r = i + (this.border ? 2 : 0), n = s + (this.border ? 2 : 0);
    this.originalHeight = n, this.originalWidth = r, this.width = r, this.height = n, 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), n = 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 f = 0; f < Math.min(l.length, s); f++) {
          const g = f + e;
          if (!t[u] || g >= t[u].length) break;
          const y = l[f];
          y === n ? t[u][g] = this.transparentChar : t[u][g] = y;
        }
      }
      return this.buffer = t, t;
    } catch (t) {
      return console.error("Art component draw() error:", t), super.draw();
    }
  }
}
function ct(o) {
  const t = [], e = o.replace(/\r\n?/g, `
`).split(`
`);
  let i = {}, s = !1, r = !1, n = [];
  const h = [], a = (f) => {
    h.push({ art: n.slice(), meta: f }), n = [];
  }, c = (f, g) => {
    try {
      return JSON.parse(f);
    } catch (y) {
      return t.push(`JSON parse error ${g}: ${y?.message ?? String(y)}`), {};
    }
  };
  for (let f = 0; f < e.length; f++) {
    const g = e[f], y = g.trimStart();
    if (!r && y.length > 0 && (r = !0, y.startsWith("§"))) {
      const w = g.slice(g.indexOf("§") + 1).trim();
      if (w) {
        const d = c(w, `in defaults at line ${f + 1}`);
        i = {
          duration: ut(d?.duration),
          loop: Yt(d?.loop),
          transparent: Xt(d?.transparent)
        };
      }
      continue;
    }
    if (y.startsWith("¶")) {
      const w = g.slice(g.indexOf("¶") + 1).trim(), d = w ? c(w, `in frame meta at line ${f + 1}`) : {}, p = {
        duration: ut(d?.duration),
        sound: typeof d?.sound == "string" ? d.sound : void 0
      };
      a(p), s = !0;
      continue;
    }
    n.push(g), g.length > 0 && (s = !0);
  }
  if (n.length && a({}), !e.some(
    (f) => f.trimStart().startsWith("§") || f.trimStart().startsWith("¶")
  ) && s) {
    const f = {
      lines: dt(e),
      meta: { duration: 0 }
      // irrelevant; there’s only one frame
    };
    return {
      defaults: { duration: 0, loop: !1 },
      frames: [f],
      errors: t
    };
  }
  const u = h.map(({ art: f, meta: g }) => {
    const y = {
      duration: g?.duration ?? i.duration ?? 100,
      sound: g?.sound
    };
    return { lines: dt(f), meta: y };
  });
  return u.length === 0 && u.push({ lines: [[]], meta: { duration: i.duration ?? 100 } }), { defaults: i, frames: u, errors: t };
}
function dt(o) {
  const t = o.slice();
  return t.length && t[0] === "" && t.shift(), t.map((i) => [...i]);
}
function et(o) {
  let t = 1, e = 1;
  for (const i of o) {
    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 ut(o) {
  return typeof o == "number" && Number.isFinite(o) ? o : void 0;
}
function Yt(o) {
  return typeof o == "boolean" ? o : !1;
}
function Xt(o) {
  return typeof o == "string" && o.length === 1 ? o : void 0;
}
class it extends v {
  // Track if component has been destroyed
  constructor(t) {
    const e = t.border ? 2 : 0, i = 12, s = 1 + e, { font: r, text: n, 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, n instanceof k ? (this.textState = n, this.text = n.value) : this.text = n || "", this.isLoading = !0, L.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((g) => g.length), 0), f = l.length;
      this.originalHeight = f + e, this.originalWidth = u + e, this.width = u + e, this.height = f + e, this.textState && this.bind(this.textState, (g) => {
        this.text = g, 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 n = t[r], h = e.glyphs.get(n);
      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(n) : 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 n = this.renderTextWithFont(this.text, this.fontAsset);
        for (let h = 0; h < Math.min(n.length, r); h++) {
          const a = n[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 f = a[l];
            t[c][u] = f;
          }
        }
        return this.buffer = t, t;
      }
      return t;
    } catch (t) {
      return console.error("Banner component draw() error:", t), super.draw();
    }
  }
}
class ft extends v {
  /**
   * Creates a new Button instance.
   *
   * @param options - Button configuration options including content and click handler
   */
  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: n, content: h, ...a } = e;
    super({
      ...a,
      width: e.width ?? e.style?.width ?? s.length + 7,
      // padding + shadow
      height: e.height ?? e.style?.height ?? 4,
      // height + shadow
      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();
    };
  }
  /**
   * Triggers the press animation.
   *
   * Sets the button to pressed state for 100ms, shifting the shadow effect
   * to provide visual feedback. Automatically returns to normal state after the animation.
   *
   * @private
   */
  press() {
    this.pressTimer && clearTimeout(this.pressTimer), this.isPressed = !0, this.pressTimer = setTimeout(() => {
      this.isPressed = !1, this.pressTimer = void 0, this.notifyAppOfFocusRefresh(), A();
    }, 100);
  }
  /**
   * Handles keyboard events for the button.
   *
   * Activates the button when Enter or Space is pressed.
   *
   * @param event - The keyboard event string (e.g., 'Enter', ' ')
   * @returns `true` if the event was handled, `false` otherwise
   */
  handleEvent(t) {
    return t === "Enter" || t === " " ? (this.press(), this.onClick?.(), !0) : !1;
  }
  /**
   * Renders the button to a 2D character buffer.
   *
   * Draws the button with:
   * - 3D shadow effect (position shifts based on press state)
   * - Border with rounded corners
   * - Centered text content
   * - Focus indicators (> and < brackets)
   * - Hotkey label (when hotkey visibility is enabled)
   *
   * @returns 2D array of characters representing the button
   */
  draw() {
    this.buffer = Array.from(
      { length: this.height },
      () => Array.from({ length: this.width }, () => this.transparentChar)
    );
    const t = (p, x, S) => {
      p >= 0 && p < this.width && x >= 0 && x < this.height && (this.buffer[x][p] = S);
    }, e = this.width - 1, i = this.height - 1;
    if (this.isPressed) {
      for (let p = 0; p < e; p++)
        t(p, 0, " ");
      for (let p = 0; p < i; p++)
        t(0, p, " ");
    } else {
      for (let p = 1; p < this.width - 1; p++)
        t(p, i, "─");
      for (let p = 1; p < this.height - 2; p++)
        t(e, p, "│");
      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 p = e, x = i;
      this.focusable && this.hasFocus, t(s + 0, r + 0, "╭"), t(s + p - 1, r + 0, "╮"), t(s + 0, r + x - 1, "╰"), t(s + p - 1, r + x - 1, "╯");
      for (let S = 1; S < p - 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 + p - 1, r + S, "│");
    }
    const n = this.border ? 1 : 0, h = this.border ? 1 : 0, a = e - n * 2, c = i - h * 2, l = this.label || "Button", u = l.length + 4, g = s + n + Math.max(Math.floor((a - u) / 2), 0) + 2, y = r + h + Math.floor(c / 2);
    for (let p = 0; p < l.length && g + p < s + e - n; p++)
      t(g + p, y, l[p]);
    let w, d;
    if (this.isPressed ? (w = "◆", d = "◆") : this.hasFocus ? (w = ">", d = "<") : (w = " ", d = " "), (this.hasFocus || this.isPressed) && (t(s + n, y, w), t(s + e - n - 1, y, d)), this.border && this.hotkey && this.isHotkeyVisibilityEnabled()) {
      const p = `[${this.hotkey.toUpperCase()}]`;
      for (let x = 0; x < p.length && x + 1 < this.width - 1; x++)
        t(s + x + 1, r + 0, p[x]);
    }
    return this.buffer;
  }
}
const tt = [
  "╭─╮╭─────",
  "│╭────",
  "╰─│╯",
  "╭│╯",
  "││",
  "││",
  "│",
  "│",
  "│"
];
function N(o) {
  const t = Math.max(...o.map((i) => i.length)), e = [];
  for (let i = 0; i < t; i++) {
    let s = "";
    for (let r = o.length - 1; r >= 0; r--) {
      const n = o[r][i] || " ";
      s += Kt(n);
    }
    e.push(s);
  }
  for (; e.length > 0 && e[e.length - 1].trim() === ""; )
    e.pop();
  return e;
}
function Kt(o) {
  return {
    "╭": "╮",
    "╮": "╯",
    "╯": "╰",
    "╰": "╭",
    "─": "│",
    "│": "─",
    " ": " "
  }[o] || o;
}
const jt = N(tt), Gt = N(N(tt)), Jt = N(N(N(tt))), qt = {
  "top-left": tt,
  "top-right": jt,
  "bottom-right": Gt,
  "bottom-left": Jt
};
class ue 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 = qt[i], r = Math.max(...s.map((h) => h.length)), n = s.length;
    super({
      width: r,
      height: n,
      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 fe extends v {
  constructor(t) {
    super({
      width: "fill",
      height: "auto",
      ...t,
      border: !1,
      background: void 0
    }), this.focusable = !1;
  }
  /**
   * Fragment renders completely transparently - only its children are visible.
   * The Fragment itself adds no visual elements.
   */
  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 n = e.x + r, h = e.y + s;
          if (n >= 0 && n < this.width && h >= 0 && h < this.height) {
            const a = i[s][r];
            a !== e.transparentChar && (this.buffer[h][n] = a);
          }
        }
    }
    return this.buffer;
  }
}
function Mt(o, t, e) {
  return t?.children && (t.children = Array.isArray(t.children) ? t.children.flat() : [t.children]), typeof o == "function" && !o.prototype?.constructor ? o(t) : new o(t);
}
class gt extends v {
  constructor(t) {
    if (super(t), this.when = t.when, t.create) {
      const e = t.create, i = t.with || {};
      this.componentFactory = () => Mt(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} />');
  }
  /**
   * Returns the component factory function if provided.
   */
  getComponentFactory() {
    return this.componentFactory;
  }
  draw() {
    return [[]];
  }
}
class pt extends v {
  constructor(t) {
    if (super(t), t.create) {
      const e = t.create, i = t.with || {};
      this.componentFactory = () => Mt(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} />");
  }
  /**
   * Returns the component factory function if provided.
   */
  getComponentFactory() {
    return this.componentFactory;
  }
  draw() {
    return [[]];
  }
}
function st(o) {
  if (typeof o != "function")
    return;
  const t = o();
  if (t instanceof v)
    return t;
}
class ge 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((n) => n instanceof gt), s = r.find((n) => n instanceof pt), 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();
  }
  /**
   * Override setChildren to capture Case and Default components in condition mode
   */
  setChildren(t) {
    this.conditionState ? (this.cases = t.filter((e) => e instanceof gt), this.defaultCase = t.find((e) => e instanceof pt), this.updateContent()) : super.setChildren(t);
  }
  /**
   * Updates the displayed content based on the current state value.
   * Clears existing children and creates the appropriate component.
   */
  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 = st(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 = st(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 = st(e);
        i && this.addChild(i);
      }
    }
    this.notifyAppOfFocusReset();
  }
}
class D extends v {
  constructor(t) {
    super({
      ...t,
      width: 0,
      // Non-visual component
      height: 0,
      border: !1,
      visible: new k(!1)
      // Explicitly invisible - not a visual component
    }), 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 k ? (this.disabledState = t.disabled, this.staticDisabled = !1) : this.staticDisabled = t.disabled ?? !1;
  }
  get disabled() {
    return this.disabledState?.value ?? this.staticDisabled;
  }
  // Override setParent to register with App when added
  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);
  }
  // Called when component is removed
  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 pe extends v {
  constructor(t) {
    super({
      ...t,
      width: 0,
      // Non-visual component
      height: 0,
      border: !1,
      visible: new k(!1)
      // Explicitly invisible - not a visual component
    }), this.registrationAttempted = !1, this.destroyed = !1, this.priority = t.priority ?? 0, t.enabled instanceof k ? (this.disabledState = t.enabled, this.staticDisabled = !1) : this.staticDisabled = !(t.enabled ?? !0), this.actionMap = /* @__PURE__ */ 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;
  }
  /**
   * Handle a button press
   * @returns true if this controller handled the button, false otherwise
   */
  handleButton(t) {
    const e = this.actionMap.get(t);
    return e ? (e(), !0) : !1;
  }
  // Override setParent to register with App when added
  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);
  }
  // Called when component is removed
  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 me extends v {
  constructor(t) {
    const e = Et(), i = Pt();
    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 Ut)
        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 f = t.getCPUUsage().toFixed(1).padStart(5, "0");
      r.push(`CPU: ${f}% |`);
    }
    if (this.showMemory) {
      const f = t.getMemoryUsage().toFixed(1).padStart(6, "0");
      r.push(`MEM: ${f}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 F 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 _t extends v {
  constructor(t) {
    const { children: e, label: i, ...s } = t;
    super({
      ...s,
      width: 0,
      height: 0,
      visible: new k(!1)
      // Data-only component, not visual
    }), this.label = i, this.children = e || [];
  }
  draw() {
    return [[]];
  }
}
class ye extends v {
  constructor(t) {
    super({
      ...t,
      height: t.height ?? t.style?.height ?? 3,
      border: t.border ?? t.style?.border ?? !0
    }), this.scrollableViewport = new vt(), 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 _t) {
        e.push({
          label: i.label,
          value: null,
          // Group headers have no value
          isGroupHeader: !0,
          isLastInGroup: !1,
          groupDepth: 0
        });
        const s = i.children;
        s.forEach((r, n) => {
          const h = n === 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;
  }
  /**
   * Move focus to the next item (skipping group headers)
   */
  moveNext() {
    let t = this.focusedIndex + 1;
    for (; t < this.items.length && this.items[t].isGroupHeader; )
      t++;
    t < this.items.length && (this.focusedIndex = t);
  }
  /**
   * Move focus to the previous item (skipping group headers)
   */
  movePrevious() {
    let t = this.focusedIndex - 1;
    for (; t >= 0 && this.items[t].isGroupHeader; )
      t--;
    t >= 0 && (this.focusedIndex = t);
  }
  /**
   * Select the currently focused item (if not a group header)
   */
  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);
  }
  /**
   * Jump to the first item
   */
  moveToStart() {
    this.focusedIndex = 0, this.items[0]?.isGroupHeader && this.moveNext();
  }
  /**
   * Jump to the last item
   */
  moveToEnd() {
    this.focusedIndex = this.items.length - 1, this.items[this.focusedIndex]?.isGroupHeader && this.movePrevious();
  }
  /**
   * Page down (move down by roughly a page of items)
   */
  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);
  }
  /**
   * Page up (move up by roughly a page of items)
   */
  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, n = 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: n,
      focusedIndex: this.focusedIndex
    }), { startIdx: c, visibleItems: l } = a;
    return l.forEach((u, f) => {
      const g = e + i + f * s, y = e;
      if (g >= t.length) {
        console.error(
          `❌ BUFFER OVERFLOW: Trying to draw at y=${g} but buffer.length=${t.length}!`
        );
        return;
      }
      const w = c + f, d = this.items[w], p = 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), f > 0 && d.groupDepth > 0 && !d.isGroupHeader) {
        const M = c + f - 1;
        if (M >= 0 && M < this.items.length) {
          const J = this.items[M];
          if (J.groupDepth > 0 && !J.isLastInGroup) {
            const Ft = g - 1;
            t[Ft][C] = "│";
          }
        }
      }
      p && this.hasFocus && (t[g][y] = ">");
      for (let M = 0; M < S.length; M++)
        t[g][C + M] = S[M];
      const P = C + S.length, H = Array.isArray(u) ? u[0] : u;
      for (let M = 0; M < H.length; M++)
        t[g][P + M] = H[M];
      if (x && !d.isGroupHeader && this.showSelectionBox) {
        t[g - 1][y + 1] = "╭", t[g][y + 1] = "│", t[g + 1][y + 1] = "╰";
        for (let M = 2; M < this.width - 4; M++)
          t[g - 1][y + M] = "─", t[g + 1][y + M] = "─";
        t[g - 1][this.width - 3] = "╮", t[g][this.width - 3] = "│", t[g + 1][this.width - 3] = "╯";
      }
    }), this.scrollableViewport.drawScrollIndicators(
      t,
      this.width,
      this.height,
      e,
      a.showUpArrow,
      a.showDownArrow
    ), this.buffer = t, t;
  }
}
class be extends v {
  constructor(t) {
    const { children: e, ...i } = t;
    super({
      ...i,
      width: 0,
      height: 0,
      visible: new k(!1)
      // Data-only component, not visual
    }), this.value = t.value, this.label = e || String(t.value);
  }
  draw() {
    return [[]];
  }
}
function Qt(o, t, e, i, s) {
  if (!o)
    return { x: 0, y: 0 };
  const r = t - i, n = e - s;
  let h = 0, a = 0;
  switch (o) {
    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(n / 2);
      break;
    case "center":
      h = Math.floor(r / 2), a = Math.floor(n / 2);
      break;
    case "right":
      h = r, a = Math.floor(n / 2);
      break;
    case "bottom-left":
      h = 0, a = n;
      break;
    case "bottom":
      h = Math.floor(r / 2), a = n;
      break;
    case "bottom-right":
      h = r, a = n;
      break;
  }
  return { x: h, y: a };
}
class b extends v {
  constructor(t) {
    let e = t.content;
    if (!e && t.children) {
      const f = Array.isArray(t.children) ? t.children : [t.children];
      f.length > 0 && (f.length === 1 ? e = f[0] : e = f);
    }
    e || (e = "");
    const { children: i, content: s, scrollable: r, wrap: n, textAlign: h, typewriter: a, typewriterSpeed: c, typewriterPauseFactor: l, ...u } = t;
    super({
      ...u,
      width: t.width,
      // Don't default to fill - let resolveSize handle it
      height: t.height
      // Let auto-sizing calculate height based on wrapped content
    }), this.scrollableViewport = new vt(), 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 = n ?? !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)
        R(e) && t.push(e);
    else R(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;
  }
  // Override resolveSize to handle width and height auto-sizing
  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 n = s + r, h = e + i, a = Math.max(1, t.availableWidth - n);
      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 (R(e)) {
          const i = e.value;
          return i == null ? "" : String(i);
        } else
          return String(e);
      }).join("");
    else if (R(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: n } = Qt(
      this.textAlign,
      t,
      e,
      s,
      i.length
    ), h = this.border ? r + 1 : r, a = this.border ? n + 1 : n;
    for (let c = 0; c < i.length && c < e; c++) {
      const l = i[c], u = a + c;
      if (u >= this.height) break;
      const f = l.length;
      let g = h;
      this.textAlign && (this.textAlign.includes("right") ? g = h + (s - f) : (this.textAlign === "center" || this.textAlign === "top" || this.textAlign === "bottom") && (g = h + Math.floor((s - f) / 2)));
      for (let y = 0; y < l.length && y < t; y++) {
        const w = g + 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 n of s) {
      if (r.length >= i) break;
      if (n.trim() === "") {
        r.push("");
        continue;
      }
      const h = n.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 mt(o) {
  return o && typeof o.value == "number";
}
class Zt 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 || mt(t.value), this.placeholder = String(t.placeholder ?? ""), this.onEnter = t.onEnter, mt(t.value) ? (this.valueNum = t.value, this.valueStr = new k(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 k(""), 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, n = this.wrapText(r, s), a = Math.max(1, n.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 n = t[r];
      n === `
` ? (i.push(s), s = "") : s.length >= e ? (i.push(s), s = n) : s += n;
    }
    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, n = this.width - (this.border ? 2 : 0), h = Math.max(0, n - 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 f = c[l];
      if (l === 0)
        for (let y = 0; y < i && y < n; y++)
          u < t.length && r + y < t[u].length && (t[u][r + y] = e[y]);
      const g = l === 0 ? r + i : r;
      for (let y = 0; y < f.length; y++) {
        const w = g + y;
        u < t.length && w < t[u].length && (t[u][w] = f[y]);
      }
    }
    if (this.hasFocus && this.cursorVisible && h > 0) {
      const { line: l, pos: u } = this.getCursorPosition(c), f = s + l, g = (l === 0 ? r + i : r) + u;
      f < t.length && g < t[f].length && (t[f][g] = "▉");
    }
    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 G 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 te extends G {
  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 n = 1; n < this.width - 1; n++)
      this.drawChar(n, 0, "─");
    this.drawChar(this.width - 1, 0, "╮"), this.drawChar(0, 1, "│");
    for (let n = 0; n < r.length && n < t; n++) {
      const h = this.hasFocus && n === i - 1 && i > 0 ? "▓" : r[n];
      this.drawChar(n + 1, 1, h);
    }
    this.drawChar(this.width - 1, 1, "│"), this.drawChar(0, 2, "╰");
    for (let n = 1; n < this.width - 1; n++)
      this.drawChar(n, 2, "─");
    if (this.drawChar(this.width - 1, 2, "╯"), this.hotkey && this.isHotkeyVisibilityEnabled()) {
      const n = `[${this.hotkey.toUpperCase()}]`;
      for (let h = 0; h < n.length && h + 1 < this.width - 1; h++)
        this.drawChar(h + 1, 0, n[h]);
    }
    return this.buffer;
  }
}
class ee extends G {
  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 n = 0; n < r.length && n + 1 < this.width - 1; n++)
        this.drawChar(n, 0, r[n]);
    }
    return this.buffer;
  }
}
class ie extends G {
  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 n = r * 2;
      r < i ? this.drawChar(n, s, (this.hasFocus, "◆")) : this.drawChar(n, s, "◇");
    }
    if (this.hotkey && this.isHotkeyVisibilityEnabled()) {
      const r = `[${this.hotkey.toUpperCase()}]`;
      for (let n = 0; n < r.length && n + 1 < this.width - 1; n++)
        this.drawChar(n, 0, r[n]);
    }
    return this.buffer;
  }
}
class se extends G {
  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 we extends G {
  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 Q extends v {
  constructor(t = {}) {
    super({
      ...t,
      width: t.width ?? t.style?.width ?? "fill",
      // Keep 'fill' for width as rows typically span horizontally
      layout: "row",
      layoutOptions: {}
    });
  }
}
class T extends v {
  constructor(t = {}) {
    super({
      ...t,
      width: t.width,
      // Don't default width - let auto-sizing calculate from children
      height: t.height,
      // Don't default height - let auto-sizing calculate from children
      layout: "column",
      layoutOptions: {}
    });
  }
}
class re extends v {
  constructor(t) {
    const {
      mapAsset: e,
      player: i,
      fogOfWar: s,
      exploredTiles: r,
      fogCharacter: n,
      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 = n ?? " ", 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 ? R(this.exploredTilesSource) ? this.exploredTilesSource.value : this.exploredTilesSource : /* @__PURE__ */ new Set();
  }
  get fogOfWar() {
    return R(this.fogOfWarSource) ? this.fogOfWarSource.value : this.fogOfWarSource;
  }
  isPositionVisible(t, e, i, s) {
    const r = Math.abs(t - i), n = Math.abs(e - s);
    return r <= 2 && n <= 1;
  }
  isPositionExplored(t, e) {
    return this.exploredTiles.has(`${t},${e}`);
  }
  addExploredPosition(t, e) {
    const i = `${t},${e}`;
    if (this.exploredTilesSource)
      if (R(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), n = 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 p of d)
        p.x >= 0 && p.x < u && p.y >= 0 && p.y < l && this.addExploredPosition(p.x, p.y);
    }
    const f = Math.max(0, e.y - c), g = Math.min(l, f + r), y = Math.max(0, e.x - a), w = Math.min(u, y + s);
    for (let d = f; d < g; d++) {
      const p = t[d];
      if (!p) continue;
      const x = d - f + h;
      if (x >= this.height) break;
      for (let S = y; S < w; S++) {
        const C = p[S];
        if (C === void 0) continue;
        const P = S - y + n;
        if (P >= this.width) break;
        if (S === e.x && d === e.y) {
          const H = this.getDirectionChar(e.direction);
          this.buffer[x][P] = H;
        } else {
          let H = C;
          if (i && i[C] && i[C].showOnMap === !1 && (H = " "), this.fogOfWar) {
            const M = this.isPositionVisible(
              S,
              d,
              e.x,
              e.y
            ), J = this.isPositionExplored(S, d);
            M || J ? this.buffer[x][P] = H : this.buffer[x][P] = this.fogCharacter;
          } else
            this.buffer[x][P] = H;
        }
      }
    }
    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 ne = {
  "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 oe {
  constructor() {
  }
  getSpriteKey(t, e) {
    return `${t}-${e}`;
  }
  // Load a material asset by name using AssetManager's State cache
  async loadMaterial(t) {
    try {
      return L.getMaterial(t);
    } catch (e) {
      return console.error(`Failed to load material "${t}":`, e), null;
    }
  }
  // Main composition method
  async compose(t, e, i, s, r = !1) {
    const n = 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 f = await this.loadMaterial(u.material);
      if (!f)
        continue;
      this.getSpriteKey(a, c);
      const g = f.layers.find(
        (w) => w.layer === a && w.position === c
      );
      if (!g)
        continue;
      const y = this.calculateSpritePosition(
        a,
        c,
        g.lines,
        g.x,
        i,
        s,
        f.placement
      );
      this.renderSprite(
        n,
        g.lines,
        y.x,
        y.y,
        i,
        s,
        r
      );
    }
    return n;
  }
  calculateSpritePosition(t, e, i, s, r, n, h) {
    const a = i.length, c = Math.max(...i.map((f) => f.length));
    let l, u;
    if (h === "ground") {
      l = Math.floor((r - c) / 2);
      const g = {
        here: 1,
        near: 7,
        middle: 10,
        far: 11
      }[t];
      u = n - g - a;
    } else if (u = Math.floor((n - a) / 2), s !== void 0)
      l = s;
    else {
      const f = `${t}-${e}`, g = ne[f];
      g !== void 0 ? l = g : 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, n, h = !1) {
    for (let a = 0; a < e.length; a++) {
      const c = s + a;
      if (c >= n || c < 0) continue;
      const l = e[a] || "";
      for (let u = 0; u < l.length; u++) {
        const f = i + u;
        if (f >= r || f < 0) continue;
        const g = l[u];
        g !== "‽" && (h && g === " " || (t[c][f] = g));
      }
    }
  }
}
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 he extends v {
  constructor(t) {
    const {
      mapAsset: e,
      player: i,
      transparency: s,
      style: r,
      ...n
    } = t;
    super({
      ...n,
      width: 28,
      // Fixed width for consistent ASCII sprite positioning
      height: 28,
      // Fixed height for consistent ASCII sprite positioning
      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 oe(), 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;
  }
  /**
   * Converts a cardinal direction into a unit vector for coordinate calculations
   *
   * Coordinate System:
   * - X axis: Horizontal (left/right) - positive X goes east (right)
   * - Y axis: Vertical (up/down) - positive Y goes south (down)
   *
   * This matches typical 2D array indexing where:
   * - map[y][x] means row Y, column X
   * - Moving "down" increases Y (going to higher row indices)
   * - Moving "right" increases X (going to higher column indices)
   */
  getDirectionVector(t) {
    switch (t) {
      case "north":
        return { dx: 0, dy: -1 };
      // Move up: decrease Y (go to earlier rows)
      case "south":
        return { dx: 0, dy: 1 };
      // Move down: increase Y (go to later rows)
      case "east":
        return { dx: 1, dy: 0 };
      // Move right: increase X (go to later columns)
      case "west":
        return { dx: -1, dy: 0 };
      // Move left: decrease X (go to earlier columns)
      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";
    }
  }
  /**
   * Cast rays using predefined offset cubes to determine what's visible in the first-person view
   *
   * Uses direction-specific raycast cubes with predefined relative offsets for each position.
   * This eliminates coordinate calculation errors and provides complete control over
   * which exact positions are checked for each direction.
   *
   * Returns the actual map character at each position (or null if occluded/out of bounds)
   */
  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 n of ["left", "center", "right"]) {
        const h = i[r][n], a = e.x + h.dx, c = e.y + h.dy;
        c >= 0 && c < t.length && a >= 0 && a < t[c].length ? s[r][n] = t[c][a] : s[r][n] = "█";
      }
    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, n = 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 + n][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 W = {
  width: "fill",
  height: "fill",
  align: "top-center",
  border: !0
}, xe = () => {
  const o = new k("Type something here!");
  return new ot(0, "demo-persistent-counter"), /* @__PURE__ */ I(T, { style: W, label: "State Basics", children: [
    /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "State (In-Memory Reactivity)" }),
    /* @__PURE__ */ m(F, { width: "90%" }),
    /* @__PURE__ */ m(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." }),
    /* @__PURE__ */ I(T, { width: "90%", align: "center", gap: { bottom: 1 }, children: [
      /* @__PURE__ */ m(Zt, { width: "90%", hotkey: "i", value: o }),
      /* @__PURE__ */ I(b, { gap: { top: 1, bottom: 1 }, children: [
        "You typed: ",
        o
      ] })
    ] }),
    /* @__PURE__ */ m(b, { width: "90%", children: "State API" }),
    /* @__PURE__ */ m(F, { width: "90%" }),
    /* @__PURE__ */ m(b, { width: "90%", gap: { left: 6 }, children: "• new State<T>(initialValue) — Create reactive state ¶ • state.value — Get or set the current values ¶" }),
    /* @__PURE__ */ m(b, { width: "90%", children: "PersistentState (localStorage Backed)" }),
    /* @__PURE__ */ m(F, { width: "90%" }),
    /* @__PURE__ */ m(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." }),
    /* @__PURE__ */ m(b, { width: "90%", children: "Tip: You can also use subscribe() for custom side effects when state changes." })
  ] });
}, Se = () => /* @__PURE__ */ I(T, { style: W, label: "Component Basics", children: [
  /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "Common Component Properties" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(b, { width: "90%", children: "All components extend the base Component class. It provides a bunch of stuff:" }),
  /* @__PURE__ */ m(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 ¶" }),
  /* @__PURE__ */ m(
    v,
    {
      gap: 1,
      width: 32,
      height: 5,
      label: "Component",
      align: "center",
      border: !0
    }
  )
] }), ve = () => /* @__PURE__ */ I(T, { style: W, label: "Text Component Basics", children: [
  /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "Common Properties" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(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 ¶" }),
  /* @__PURE__ */ m(b, { width: "90%", children: "Text Alignment" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ I(Q, { width: 15, align: "center", children: [
    /* @__PURE__ */ m(b, { height: 5, width: 5, border: !0, textAlign: "top-left", children: "A" }),
    /* @__PURE__ */ m(b, { width: 5, height: 5, border: !0, textAlign: "top", children: "B" }),
    /* @__PURE__ */ m(b, { width: 5, height: 5, border: !0, textAlign: "top-right", children: "C" })
  ] }),
  /* @__PURE__ */ I(Q, { width: 15, align: "center", children: [
    /* @__PURE__ */ m(b, { height: 5, width: 5, border: !0, textAlign: "left", children: "D" }),
    /* @__PURE__ */ m(b, { width: 5, height: 5, border: !0, textAlign: "center", children: "E" }),
    /* @__PURE__ */ m(b, { width: 5, height: 5, border: !0, textAlign: "right", children: "F" })
  ] }),
  /* @__PURE__ */ I(Q, { width: 15, align: "center", children: [
    /* @__PURE__ */ m(b, { height: 5, width: 5, border: !0, textAlign: "bottom-left", children: "G" }),
    /* @__PURE__ */ m(b, { width: 5, height: 5, border: !0, textAlign: "bottom", children: "H" }),
    /* @__PURE__ */ m(b, { width: 5, height: 5, border: !0, textAlign: "bottom-right", children: "I" })
  ] })
] }), Ce = () => {
  const o = new k(
    "Press X on the keyboard"
  ), t = (r) => {
    o.value = r, setTimeout(() => {
      o.value = "Press X on the keyboard";
    }, 2e3);
  }, e = () => {
    const r = Math.floor(Math.random() * 60) + 10, n = Math.floor(Math.random() * 20) + 5, h = new At({
      sprite: "firework",
      position: { x: r, y: n }
    });
    s.addChild(h), setTimeout(() => {
      s.removeChild(h);
    }, 800);
  }, s = /* @__PURE__ */ I(T, { style: W, label: "Keybindings & Mobile Basics", children: [
    /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "Keybindings" }),
    /* @__PURE__ */ m(F, { width: "90%" }),
    /* @__PURE__ */ m(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." }),
    /* @__PURE__ */ m(D, { keyBinding: "x", action: () => {
      t("X pressed - Firework launched!"), e();
    } }),
    /* @__PURE__ */ m(T, { width: "90%", align: "center", border: !0, gap: { bottom: 1 }, children: /* @__PURE__ */ m(
      b,
      {
        align: "center",
        textAlign: "center",
        width: "80%",
        gap: { top: 1, bottom: 1 },
        children: o
      }
    ) }),
    /* @__PURE__ */ m(b, { width: "90%", children: "Keybind Properties" }),
    /* @__PURE__ */ m(F, { width: "90%" }),
    /* @__PURE__ */ m(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) ¶' }),
    /* @__PURE__ */ m(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;
}, Ae = () => {
  const o = new k(5);
  return /* @__PURE__ */ I(T, { style: W, label: "Navigation Basics", children: [
    /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "Navigation Keys" }),
    /* @__PURE__ */ m(F, { width: "90%" }),
    /* @__PURE__ */ I(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."
    ] }),
    /* @__PURE__ */ m(b, { width: "90%", gap: { left: 6 }, children: "• Tab — focus on next focusable component ¶ • Shift+Tab — focus on previous focusable component ¶ • Space — press button ¶" }),
    /* @__PURE__ */ m(b, { width: "90%", gap: { bottom: 1 }, children: "Try navigating between these two buttons using [Tab], [Shift+Tab]" }),
    /* @__PURE__ */ I(T, { width: "90%", align: "center", gap: { bottom: 1 }, children: [
      /* @__PURE__ */ m(
        te,
        {
          width: 50,
          align: "center",
          gap: { bottom: 1 },
          value: o,
          min: 0,
          max: 20,
          readonly: !0
        }
      ),
      /* @__PURE__ */ m(
        se,
        {
          width: 50,
          align: "center",
          gap: { bottom: 1 },
          value: o,
          min: 0,
          max: 20,
          readonly: !0
        }
      ),
      /* @__PURE__ */ m(
        ee,
        {
          width: 50,
          align: "center",
          gap: { bottom: 1 },
          value: o,
          min: 0,
          max: 20,
          readonly: !0
        }
      ),
      /* @__PURE__ */ m(
        ie,
        {
          width: 50,
          align: "center",
          gap: { bottom: 1 },
          value: o,
          min: 0,
          max: 20,
          readonly: !0
        }
      )
    ] }),
    /* @__PURE__ */ I(Q, { width: "fill", align: "center", gap: { bottom: 2 }, children: [
      /* @__PURE__ */ m(
        ft,
        {
          height: 6,
          hotkey: "a",
          onClick: () => {
            o.value > 0 && o.value--;
          },
          children: "Decrement"
        }
      ),
      /* @__PURE__ */ m(
        ft,
        {
          height: 6,
          hotkey: "s",
          onClick: () => {
            o.value < 20 && o.value++;
          },
          children: "Increment"
        }
      )
    ] })
  ] });
}, Me = () => /* @__PURE__ */ I(T, { style: W, label: "Sprites Basics", children: [
  /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "Sprites" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(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." }),
  /* @__PURE__ */ m(At, { sprite: "beating-heart" }),
  /* @__PURE__ */ m(b, { width: "90%", children: "Using Sprites" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(b, { width: "90%", gap: { left: 4 }, children: "Display sprites using the Art component:" }),
  /* @__PURE__ */ m(
    b,
    {
      width: 56,
      textAlign: "center",
      border: !0,
      align: "center",
      gap: { left: 4, top: 1, bottom: 1 },
      children: '<Art sprite="beating-heart" />'
    }
  ),
  /* @__PURE__ */ m(b, { width: "90%", children: "Tip: To learn how to create your own sprites, check out the documentation in the public/art/sprites directory." })
] }), ke = () => /* @__PURE__ */ I(T, { style: W, label: "Banners", children: [
  /* @__PURE__ */ m(it, { font: "pencil", text: "pencil", letterSpacing: 0, align: "center" }),
  /* @__PURE__ */ m(it, { font: "marker", text: "marker", letterSpacing: 1, align: "center" }),
  /* @__PURE__ */ m(it, { font: "pixel", text: "pixel", letterSpacing: 1, align: "center" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(b, { width: "90%", gap: { left: 4 }, children: "Display sprites using the Art component:" }),
  /* @__PURE__ */ m(
    b,
    {
      width: 56,
      textAlign: "center",
      border: !0,
      align: "center",
      gap: { left: 4, top: 1, bottom: 1 },
      children: '<Banner font="marker" text="marker" />'
    }
  ),
  /* @__PURE__ */ m(b, { width: "90%", gap: { bottom: 2 }, children: "TIP: Fonts for banners are stored in the public/art/fonts/ directory with the .art extension." })
] }), at = new k(null), kt = new k({
  x: 15,
  y: 5,
  direction: "east"
});
L.getMap("example").then((o) => {
  at.value = o;
}).catch((o) => {
  console.error("Failed to load map:", o);
});
const q = new Ct({
  mapAsset: at,
  player: kt
}), le = new k(/* @__PURE__ */ new Set()), Ie = () => /* @__PURE__ */ I(T, { style: W, label: "Maps Basics", children: [
  /* @__PURE__ */ m(D, { keyBinding: "w", action: () => q.moveForward() }),
  /* @__PURE__ */ m(D, { keyBinding: "s", action: () => q.moveBackward() }),
  /* @__PURE__ */ m(D, { keyBinding: "a", action: () => q.turnLeft() }),
  /* @__PURE__ */ m(D, { keyBinding: "d", action: () => q.turnRight() }),
  /* @__PURE__ */ m(b, { width: "90%", gap: { top: 1 }, children: "Maps" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(b, { width: "90%", children: "Maps are ASCII layouts that define game environments, combining visual representation with legend files that specify gameplay properties." }),
  /* @__PURE__ */ I(T, { width: "90%", align: "center", gap: { top: 1, bottom: 1 }, children: [
    /* @__PURE__ */ m(
      re,
      {
        mapAsset: at,
        player: kt,
        fogOfWar: !1,
        exploredTiles: le,
        width: "fill",
        height: 15
      }
    ),
    /* @__PURE__ */ m(b, { children: "[W] forward • [S] backward • [A] turn left • [D] turn right" })
  ] }),
  /* @__PURE__ */ m(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." })
] }), ht = new k(null), It = new k({
  x: 2,
  y: 1,
  direction: "east"
});
L.getMap("example").then((o) => {
  ht.value = o;
}).catch((o) => {
  console.error("Failed to load map:", o);
});
const _ = new Ct({
  mapAsset: ht,
  player: It
}), Fe = () => /* @__PURE__ */ I(T, { style: W, label: "First Person View Basics", children: [
  /* @__PURE__ */ m(D, { keyBinding: "w", action: () => _.moveForward() }),
  /* @__PURE__ */ m(D, { keyBinding: "s", action: () => _.moveBackward() }),
  /* @__PURE__ */ m(D, { keyBinding: "a", action: () => _.turnLeft() }),
  /* @__PURE__ */ m(D, { keyBinding: "d", action: () => _.turnRight() }),
  /* @__PURE__ */ m(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." }),
  /* @__PURE__ */ m(b, { width: "90%", children: "Interactive First Person Example" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(b, { width: "90%", gap: { bottom: 1 }, children: "Controls: [W] forward • [S] backward • [A] turn left • [D] turn right" }),
  /* @__PURE__ */ m(
    he,
    {
      align: "center",
      gap: { bottom: 2 },
      mapAsset: ht,
      player: It
    }
  ),
  /* @__PURE__ */ m(b, { width: "90%", children: "Raycasting System" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(b, { width: "90%", gap: { left: 4 }, children: "FirstPersonView uses raycasting with predefined offset cubes to determine what the player can see in each direction:" }),
  /* @__PURE__ */ m(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" }),
  /* @__PURE__ */ m(b, { width: "90%", gap: { top: 2 }, children: "Scene Switching" }),
  /* @__PURE__ */ m(F, { width: "90%" }),
  /* @__PURE__ */ m(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." })
] });
export {
  Ut as App,
  At as Art,
  L as AssetManager,
  it as Banner,
  ke as BannerBasics,
  se as BarSlider,
  ft as Button,
  gt as Case,
  ue as CelticBorder,
  T as Column,
  v as Component,
  Se as ComponentBasics,
  pt as Default,
  ie as DotSlider,
  Fe as FPVBasics,
  oe as FirstPersonCompositor,
  he as FirstPersonView,
  fe as Fragment,
  ee as GaugeSlider,
  Ct as GridMovement,
  D as Keybind,
  Ce as KeybindingsBasics,
  F as Line,
  re as MapView,
  Ie as MapsBasics,
  pe as MobileController,
  Ae as NavigationBasics,
  be as Option,
  _t as OptionGroup,
  me as PerfMonitor,
  ot as PersistentState,
  te as ProgressBarSlider,
  Q as Row,
  vt as ScrollableViewport,
  ye as Select,
  Z as SoundManager,
  Me as SpritesBasics,
  k as State,
  xe as StateBasics,
  ge as Switch,
  b as Text,
  ve as TextBasics,
  Zt as TextInput,
  we as VerticalSlider,
  Et as isCPUSupported,
  Pt as isMemorySupported,
  Tt as isMobileDevice,
  wt as isNodeEnvironment,
  R as isState,
  V as isWebEnvironment,
  Y as loadArt,
  xt as setupKeyboardHandling,
  de as start,
  St as validateWebEnvironment
};