// @bun
import {
  ANSI,
  CliRenderer,
  TreeSitterClient,
  resolveRenderLib
} from "./index-p0v1qbn9.js";

// src/testing/test-renderer.ts
import { Readable, Writable } from "stream";

// src/testing/mock-keys.ts
import { Buffer as Buffer2 } from "buffer";
function pasteBytes(text) {
  return Uint8Array.from(Buffer2.from(text));
}
var KeyCodes = {
  RETURN: "\r",
  LINEFEED: `
`,
  TAB: "\t",
  BACKSPACE: "\b",
  DELETE: "\x1B[3~",
  HOME: "\x1B[H",
  END: "\x1B[F",
  ESCAPE: "\x1B",
  ARROW_UP: "\x1B[A",
  ARROW_DOWN: "\x1B[B",
  ARROW_RIGHT: "\x1B[C",
  ARROW_LEFT: "\x1B[D",
  F1: "\x1BOP",
  F2: "\x1BOQ",
  F3: "\x1BOR",
  F4: "\x1BOS",
  F5: "\x1B[15~",
  F6: "\x1B[17~",
  F7: "\x1B[18~",
  F8: "\x1B[19~",
  F9: "\x1B[20~",
  F10: "\x1B[21~",
  F11: "\x1B[23~",
  F12: "\x1B[24~"
};
var kittyKeyCodeMap = {
  escape: 27,
  tab: 9,
  return: 13,
  backspace: 127,
  insert: 57348,
  delete: 57349,
  left: 57350,
  right: 57351,
  up: 57352,
  down: 57353,
  pageup: 57354,
  pagedown: 57355,
  home: 57356,
  end: 57357,
  f1: 57364,
  f2: 57365,
  f3: 57366,
  f4: 57367,
  f5: 57368,
  f6: 57369,
  f7: 57370,
  f8: 57371,
  f9: 57372,
  f10: 57373,
  f11: 57374,
  f12: 57375
};
function encodeKittySequence(codepoint, modifiers) {
  let modMask = 0;
  if (modifiers?.shift)
    modMask |= 1;
  if (modifiers?.meta)
    modMask |= 2;
  if (modifiers?.ctrl)
    modMask |= 4;
  if (modifiers?.super)
    modMask |= 8;
  if (modifiers?.hyper)
    modMask |= 16;
  if (modMask === 0) {
    return `\x1B[${codepoint}u`;
  } else {
    return `\x1B[${codepoint};${modMask + 1}u`;
  }
}
function encodeModifyOtherKeysSequence(charCode, modifiers) {
  let modMask = 0;
  if (modifiers?.shift)
    modMask |= 1;
  if (modifiers?.meta)
    modMask |= 2;
  if (modifiers?.ctrl)
    modMask |= 4;
  if (modifiers?.super)
    modMask |= 8;
  if (modifiers?.hyper)
    modMask |= 16;
  if (modMask === 0) {
    return String.fromCharCode(charCode);
  }
  return `\x1B[27;${modMask + 1};${charCode}~`;
}
function resolveKeyInput(key) {
  let keyValue;
  let keyName;
  if (typeof key === "string") {
    if (key in KeyCodes) {
      keyValue = KeyCodes[key];
      keyName = key.toLowerCase();
    } else {
      keyValue = key;
      keyName = undefined;
    }
  } else {
    keyValue = KeyCodes[key];
    if (!keyValue) {
      throw new Error(`Unknown key: ${key}`);
    }
    keyName = String(key).toLowerCase();
  }
  return { keyValue, keyName };
}
function createMockKeys(renderer, options) {
  const useKittyKeyboard = options?.kittyKeyboard ?? false;
  const useOtherModifiersMode = options?.otherModifiersMode ?? false;
  const effectiveOtherModifiersMode = useOtherModifiersMode && !useKittyKeyboard;
  const pressKeys = async (keys, delayMs = 0) => {
    for (const key of keys) {
      const { keyValue: keyCode } = resolveKeyInput(key);
      renderer.stdin.emit("data", Buffer2.from(keyCode));
      if (delayMs > 0) {
        await new Promise((resolve) => setTimeout(resolve, delayMs));
      }
    }
  };
  const pressKey = (key, modifiers) => {
    if (useKittyKeyboard) {
      let { keyValue, keyName } = resolveKeyInput(key);
      const valueToKeyNameMap = {
        "\b": "backspace",
        "\r": "return",
        "\n": "return",
        "\t": "tab",
        "\x1B": "escape",
        "\x1B[A": "up",
        "\x1B[B": "down",
        "\x1B[C": "right",
        "\x1B[D": "left",
        "\x1B[H": "home",
        "\x1B[F": "end",
        "\x1B[3~": "delete"
      };
      if (keyValue && valueToKeyNameMap[keyValue]) {
        keyName = valueToKeyNameMap[keyValue];
      }
      if (keyName && keyName.startsWith("arrow_")) {
        keyName = keyName.substring(6);
      }
      if (keyName && kittyKeyCodeMap[keyName]) {
        const kittyCode = kittyKeyCodeMap[keyName];
        const sequence = encodeKittySequence(kittyCode, modifiers);
        renderer.stdin.emit("data", Buffer2.from(sequence));
        return;
      }
      if (keyValue && keyValue.length === 1 && !keyValue.startsWith("\x1B")) {
        const codepoint = keyValue.codePointAt(0);
        if (codepoint) {
          const sequence = encodeKittySequence(codepoint, modifiers);
          renderer.stdin.emit("data", Buffer2.from(sequence));
          return;
        }
      }
    }
    if (effectiveOtherModifiersMode && modifiers) {
      let { keyValue, keyName } = resolveKeyInput(key);
      const valueToCharCodeMap = {
        "\b": 127,
        "\r": 13,
        "\n": 13,
        "\t": 9,
        "\x1B": 27,
        " ": 32
      };
      let charCode;
      if (keyValue && valueToCharCodeMap[keyValue] !== undefined) {
        charCode = valueToCharCodeMap[keyValue];
      } else if (keyValue && keyValue.length === 1 && !keyValue.startsWith("\x1B")) {
        charCode = keyValue.charCodeAt(0);
      }
      if (charCode !== undefined) {
        const sequence = encodeModifyOtherKeysSequence(charCode, modifiers);
        renderer.stdin.emit("data", Buffer2.from(sequence));
        return;
      }
    }
    let keyCode = resolveKeyInput(key).keyValue;
    if (modifiers) {
      if (keyCode.startsWith("\x1B[") && keyCode.length > 2) {
        const modifier = 1 + (modifiers.shift ? 1 : 0) + (modifiers.meta ? 2 : 0) + (modifiers.ctrl ? 4 : 0) + (modifiers.super ? 8 : 0) + (modifiers.hyper ? 16 : 0);
        if (modifier > 1) {
          const tildeMatch = keyCode.match(/^\x1b\[(\d+)~$/);
          if (tildeMatch) {
            keyCode = `\x1B[${tildeMatch[1]};${modifier}~`;
          } else {
            const ending = keyCode.slice(-1);
            keyCode = `\x1B[1;${modifier}${ending}`;
          }
        }
      } else if (keyCode.length === 1) {
        let char = keyCode;
        if (char === "\t" && modifiers.shift) {
          keyCode = modifiers.meta ? "\x1B\x1B[Z" : "\x1B[Z";
          renderer.stdin.emit("data", Buffer2.from(keyCode));
          return;
        }
        if (char === "\b" && (modifiers.ctrl || modifiers.super || modifiers.hyper)) {
          const modifier = 1 + (modifiers.shift ? 1 : 0) + (modifiers.meta ? 2 : 0) + (modifiers.ctrl ? 4 : 0) + (modifiers.super ? 8 : 0) + (modifiers.hyper ? 16 : 0);
          keyCode = `\x1B[27;${modifier};127~`;
        } else if (modifiers.ctrl) {
          if (char >= "a" && char <= "z") {
            keyCode = String.fromCharCode(char.charCodeAt(0) - 96);
          } else if (char >= "A" && char <= "Z") {
            keyCode = String.fromCharCode(char.charCodeAt(0) - 64);
          } else {
            const specialCtrlMap = {
              "[": "\x1B",
              "\\": "\x1C",
              "]": "\x1D",
              "^": "\x1E",
              _: "\x1F",
              "?": "\x7F",
              "/": "\x1F",
              "-": "\x1F",
              ".": "\x1E",
              ",": "\x1C",
              "@": "\x00",
              " ": "\x00"
            };
            if (char in specialCtrlMap) {
              keyCode = specialCtrlMap[char];
            }
          }
          if (modifiers.meta) {
            keyCode = `\x1B${keyCode}`;
          }
        } else {
          if (modifiers.shift && char >= "a" && char <= "z") {
            char = char.toUpperCase();
          }
          if (modifiers.meta) {
            keyCode = `\x1B${char}`;
          } else {
            keyCode = char;
          }
        }
      } else if (modifiers.meta && !keyCode.startsWith("\x1B")) {
        keyCode = `\x1B${keyCode}`;
      }
    }
    renderer.stdin.emit("data", Buffer2.from(keyCode));
  };
  const typeText = async (text, delayMs = 0) => {
    const keys = text.split("");
    await pressKeys(keys, delayMs);
  };
  const pressReturn = (modifiers) => {
    pressKey(KeyCodes.RETURN, modifiers);
  };
  const pressEscape = (modifiers) => {
    pressKey(KeyCodes.ESCAPE, modifiers);
  };
  const pressTab = (modifiers) => {
    pressKey(KeyCodes.TAB, modifiers);
  };
  const pressBackspace = (modifiers) => {
    pressKey(KeyCodes.BACKSPACE, modifiers);
  };
  const pressArrow = (direction, modifiers) => {
    const keyMap = {
      up: KeyCodes.ARROW_UP,
      down: KeyCodes.ARROW_DOWN,
      left: KeyCodes.ARROW_LEFT,
      right: KeyCodes.ARROW_RIGHT
    };
    pressKey(keyMap[direction], modifiers);
  };
  const pressCtrlC = () => {
    pressKey("c", { ctrl: true });
  };
  const pasteBracketedText = (text) => {
    return pressKeys([ANSI.bracketedPasteStart, text, ANSI.bracketedPasteEnd]);
  };
  return {
    pressKeys,
    pressKey,
    typeText,
    pressEnter: pressReturn,
    pressEscape,
    pressTab,
    pressBackspace,
    pressArrow,
    pressCtrlC,
    pasteBracketedText
  };
}

// src/testing/mock-mouse.ts
var MouseButtons = {
  LEFT: 0,
  MIDDLE: 1,
  RIGHT: 2,
  WHEEL_UP: 64,
  WHEEL_DOWN: 65,
  WHEEL_LEFT: 66,
  WHEEL_RIGHT: 67
};
function createMockMouse(renderer) {
  let currentPosition = { x: 0, y: 0 };
  let buttonsPressed = new Set;
  const generateMouseEvent = (type, x, y, button = MouseButtons.LEFT, modifiers = {}) => {
    let buttonCode = button;
    if (modifiers.shift)
      buttonCode |= 4;
    if (modifiers.alt)
      buttonCode |= 8;
    if (modifiers.ctrl)
      buttonCode |= 16;
    switch (type) {
      case "move":
        buttonCode = 32 | 3;
        if (modifiers.shift)
          buttonCode |= 4;
        if (modifiers.alt)
          buttonCode |= 8;
        if (modifiers.ctrl)
          buttonCode |= 16;
        break;
      case "drag":
        buttonCode = (buttonsPressed.size > 0 ? Array.from(buttonsPressed)[0] : button) | 32;
        if (modifiers.shift)
          buttonCode |= 4;
        if (modifiers.alt)
          buttonCode |= 8;
        if (modifiers.ctrl)
          buttonCode |= 16;
        break;
      case "scroll":
        break;
    }
    const ansiX = x + 1;
    const ansiY = y + 1;
    let pressRelease = "M";
    if (type === "up" || type === "move" || type === "drag") {
      pressRelease = "m";
    }
    return `\x1B[<${buttonCode};${ansiX};${ansiY}${pressRelease}`;
  };
  const emitMouseEvent = async (type, x, y, button = MouseButtons.LEFT, options = {}) => {
    const { modifiers = {}, delayMs = 0 } = options;
    const eventSequence = generateMouseEvent(type, x, y, button, modifiers);
    renderer.stdin.emit("data", Buffer.from(eventSequence));
    currentPosition = { x, y };
    if (type === "down" && button < 64) {
      buttonsPressed.add(button);
    } else if (type === "up") {
      buttonsPressed.delete(button);
    }
    if (delayMs > 0) {
      await new Promise((resolve) => setTimeout(resolve, delayMs));
    }
  };
  const moveTo = async (x, y, options = {}) => {
    const { button = MouseButtons.LEFT, delayMs = 0, modifiers = {} } = options;
    if (buttonsPressed.size > 0) {
      await emitMouseEvent("drag", x, y, Array.from(buttonsPressed)[0], { modifiers, delayMs });
    } else {
      await emitMouseEvent("move", x, y, button, { modifiers, delayMs });
    }
    currentPosition = { x, y };
  };
  const click = async (x, y, button = MouseButtons.LEFT, options = {}) => {
    const { delayMs = 10, modifiers = {} } = options;
    await emitMouseEvent("down", x, y, button, { modifiers, delayMs });
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    await emitMouseEvent("up", x, y, button, { modifiers, delayMs });
  };
  const doubleClick = async (x, y, button = MouseButtons.LEFT, options = {}) => {
    const { delayMs = 10, modifiers = {} } = options;
    await click(x, y, button, { modifiers, delayMs });
    await new Promise((resolve) => setTimeout(resolve, delayMs));
    await click(x, y, button, { modifiers, delayMs });
  };
  const pressDown = async (x, y, button = MouseButtons.LEFT, options = {}) => {
    const { modifiers = {}, delayMs = 0 } = options;
    await emitMouseEvent("down", x, y, button, { modifiers, delayMs });
  };
  const release = async (x, y, button = MouseButtons.LEFT, options = {}) => {
    const { modifiers = {}, delayMs = 0 } = options;
    await emitMouseEvent("up", x, y, button, { modifiers, delayMs });
  };
  const drag = async (startX, startY, endX, endY, button = MouseButtons.LEFT, options = {}) => {
    const { delayMs = 10, modifiers = {} } = options;
    await pressDown(startX, startY, button, { modifiers });
    const steps = 5;
    const dx = (endX - startX) / steps;
    const dy = (endY - startY) / steps;
    for (let i = 1;i <= steps; i++) {
      const currentX = Math.round(startX + dx * i);
      const currentY = Math.round(startY + dy * i);
      await emitMouseEvent("drag", currentX, currentY, button, { modifiers, delayMs });
    }
    await release(endX, endY, button, { modifiers });
  };
  const scroll = async (x, y, direction, options = {}) => {
    const { modifiers = {}, delayMs = 0 } = options;
    let button;
    switch (direction) {
      case "up":
        button = MouseButtons.WHEEL_UP;
        break;
      case "down":
        button = MouseButtons.WHEEL_DOWN;
        break;
      case "left":
        button = MouseButtons.WHEEL_LEFT;
        break;
      case "right":
        button = MouseButtons.WHEEL_RIGHT;
        break;
    }
    await emitMouseEvent("scroll", x, y, button, { modifiers, delayMs });
  };
  const getCurrentPosition = () => {
    return { ...currentPosition };
  };
  const getPressedButtons = () => {
    return Array.from(buttonsPressed);
  };
  return {
    moveTo,
    click,
    doubleClick,
    pressDown,
    release,
    drag,
    scroll,
    getCurrentPosition,
    getPressedButtons,
    emitMouseEvent
  };
}

// src/testing/test-renderer.ts
var decoder = new TextDecoder;

class TestWriteStream extends Writable {
  isTTY = true;
  columns;
  rows;
  constructor(columns, rows) {
    super();
    this.columns = columns;
    this.rows = rows;
  }
  _write(_chunk, _encoding, callback) {
    callback();
  }
  getColorDepth() {
    return 24;
  }
}
async function createTestRenderer(options) {
  const useKittyKeyboard = options.kittyKeyboard ? { events: true } : options.useKittyKeyboard;
  const renderer = await setupTestRenderer({
    ...options,
    useKittyKeyboard,
    screenMode: options.screenMode ?? "main-screen",
    footerHeight: options.footerHeight ?? 12,
    consoleMode: options.consoleMode ?? "disabled",
    externalOutputMode: options.externalOutputMode ?? "passthrough"
  });
  const mockInput = createMockKeys(renderer, {
    kittyKeyboard: options.kittyKeyboard,
    otherModifiersMode: options.otherModifiersMode
  });
  const mockMouse = createMockMouse(renderer);
  const renderOnce = async () => {
    await renderer.loop();
  };
  return {
    renderer,
    mockInput,
    mockMouse,
    renderOnce,
    captureCharFrame: () => {
      const currentBuffer = renderer.currentRenderBuffer;
      const frameBytes = currentBuffer.getRealCharBytes(true);
      return decoder.decode(frameBytes);
    },
    captureSpans: () => {
      const currentBuffer = renderer.currentRenderBuffer;
      const lines = currentBuffer.getSpanLines();
      const cursorState = renderer.getCursorState();
      return {
        cols: currentBuffer.width,
        rows: currentBuffer.height,
        cursor: [cursorState.x, cursorState.y],
        lines
      };
    },
    resize: (width, height) => {
      renderer.processResize(width, height);
    }
  };
}
async function setupTestRenderer(config) {
  const stdin = config.stdin || new Readable({ read() {} });
  const width = config.width || config.stdout?.columns || process.stdout.columns || 80;
  const height = config.height || config.stdout?.rows || process.stdout.rows || 24;
  const stdout = config.stdout || new TestWriteStream(width, height);
  const renderHeight = config.screenMode === "split-footer" ? config.footerHeight ?? 12 : height;
  const ziglib = resolveRenderLib();
  const rendererPtr = ziglib.createRenderer(width, renderHeight, {
    testing: true,
    remote: config.remote ?? false
  });
  if (!rendererPtr) {
    throw new Error("Failed to create test renderer");
  }
  if (config.useThread === undefined) {
    config.useThread = true;
  }
  if (process.platform === "linux") {
    config.useThread = false;
  }
  ziglib.setUseThread(rendererPtr, config.useThread);
  const renderer = new CliRenderer(ziglib, rendererPtr, stdin, stdout, width, height, config);
  process.off("SIGWINCH", renderer["sigwinchHandler"]);
  return renderer;
}
// src/testing/mock-tree-sitter-client.ts
class MockTreeSitterClient extends TreeSitterClient {
  _highlightPromises = [];
  _mockResult = { highlights: [] };
  _autoResolveTimeout;
  constructor(options) {
    super({ dataPath: "/tmp/mock" });
    this._autoResolveTimeout = options?.autoResolveTimeout;
  }
  async highlightOnce(content, filetype) {
    const { promise, resolve } = Promise.withResolvers();
    let timeout;
    if (this._autoResolveTimeout !== undefined) {
      timeout = setTimeout(() => {
        const index = this._highlightPromises.findIndex((p) => p.promise === promise);
        if (index !== -1) {
          resolve(this._mockResult);
          this._highlightPromises.splice(index, 1);
        }
      }, this._autoResolveTimeout);
    }
    this._highlightPromises.push({ promise, resolve, timeout });
    return promise;
  }
  setMockResult(result) {
    this._mockResult = result;
  }
  resolveHighlightOnce(index = 0) {
    if (index >= 0 && index < this._highlightPromises.length) {
      const item = this._highlightPromises[index];
      if (item.timeout) {
        clearTimeout(item.timeout);
      }
      item.resolve(this._mockResult);
      this._highlightPromises.splice(index, 1);
    }
  }
  resolveAllHighlightOnce() {
    for (const { resolve, timeout } of this._highlightPromises) {
      if (timeout) {
        clearTimeout(timeout);
      }
      resolve(this._mockResult);
    }
    this._highlightPromises = [];
  }
  isHighlighting() {
    return this._highlightPromises.length > 0;
  }
}
// src/testing/spy.ts
function createSpy() {
  const calls = [];
  const spy = (...args) => {
    calls.push(args);
  };
  spy.calls = calls;
  spy.callCount = () => calls.length;
  spy.calledWith = (...expected) => {
    return calls.some((call) => JSON.stringify(call) === JSON.stringify(expected));
  };
  spy.reset = () => calls.length = 0;
  return spy;
}
// src/testing/test-recorder.ts
class TestRecorder {
  renderer;
  frames = [];
  recording = false;
  frameNumber = 0;
  startTime = 0;
  originalRenderNative;
  decoder = new TextDecoder;
  recordBuffers;
  now;
  constructor(renderer, options) {
    this.renderer = renderer;
    this.recordBuffers = options?.recordBuffers || {};
    this.now = options?.now ?? (() => performance.now());
  }
  rec() {
    if (this.recording) {
      return;
    }
    this.recording = true;
    this.frames = [];
    this.frameNumber = 0;
    this.startTime = this.now();
    this.originalRenderNative = this.renderer["renderNative"].bind(this.renderer);
    this.renderer["renderNative"] = () => {
      this.originalRenderNative();
      this.captureFrame();
    };
  }
  stop() {
    if (!this.recording) {
      return;
    }
    this.recording = false;
    if (this.originalRenderNative) {
      this.renderer["renderNative"] = this.originalRenderNative;
      this.originalRenderNative = undefined;
    }
  }
  get recordedFrames() {
    return [...this.frames];
  }
  clear() {
    this.frames = [];
    this.frameNumber = 0;
  }
  get isRecording() {
    return this.recording;
  }
  captureFrame() {
    const currentBuffer = this.renderer.currentRenderBuffer;
    const frameBytes = currentBuffer.getRealCharBytes(true);
    const frame = this.decoder.decode(frameBytes);
    const recordedFrame = {
      frame,
      timestamp: this.now() - this.startTime,
      frameNumber: this.frameNumber++
    };
    if (this.recordBuffers.fg || this.recordBuffers.bg || this.recordBuffers.attributes) {
      const buffers = currentBuffer.buffers;
      recordedFrame.buffers = {};
      if (this.recordBuffers.fg) {
        recordedFrame.buffers.fg = new Float32Array(buffers.fg);
      }
      if (this.recordBuffers.bg) {
        recordedFrame.buffers.bg = new Float32Array(buffers.bg);
      }
      if (this.recordBuffers.attributes) {
        recordedFrame.buffers.attributes = new Uint8Array(buffers.attributes);
      }
    }
    this.frames.push(recordedFrame);
  }
}
export {
  pasteBytes,
  createTestRenderer,
  createSpy,
  createMockMouse,
  createMockKeys,
  TestRecorder,
  MouseButtons,
  MockTreeSitterClient,
  KeyCodes
};

//# debugId=75C57AE1327A974C64756E2164756E21
//# sourceMappingURL=testing.js.map
