const commands = Object.create(null);
const register = commandMap => {
  Object.assign(commands, commandMap);
};
const getCommand = key => {
  return commands[key];
};
const execute = (command, ...args) => {
  const fn = getCommand(command);
  if (!fn) {
    throw new Error(`command not found ${command}`);
  }
  return fn(...args);
};

class AssertionError extends Error {
  constructor(message) {
    super(message);
    this.name = 'AssertionError';
  }
}
const getType = value => {
  switch (typeof value) {
    case 'number':
      return 'number';
    case 'function':
      return 'function';
    case 'string':
      return 'string';
    case 'object':
      if (value === null) {
        return 'null';
      }
      if (Array.isArray(value)) {
        return 'array';
      }
      return 'object';
    case 'boolean':
      return 'boolean';
    default:
      return 'unknown';
  }
};
const object = value => {
  const type = getType(value);
  if (type !== 'object') {
    throw new AssertionError('expected value to be of type object');
  }
};
const number = value => {
  const type = getType(value);
  if (type !== 'number') {
    throw new AssertionError('expected value to be of type number');
  }
};
const string = value => {
  const type = getType(value);
  if (type !== 'string') {
    throw new AssertionError('expected value to be of type string');
  }
};

const Two = '2.0';
const create$4$1 = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};
const callbacks = Object.create(null);
const set$2 = (id, fn) => {
  callbacks[id] = fn;
};
const get$2 = id => {
  return callbacks[id];
};
const remove = id => {
  delete callbacks[id];
};
let id = 0;
const create$3$1 = () => {
  return ++id;
};
const registerPromise = () => {
  const id = create$3$1();
  const {
    resolve,
    promise
  } = Promise.withResolvers();
  set$2(id, resolve);
  return {
    id,
    promise
  };
};
const create$2$1 = (method, params) => {
  const {
    id,
    promise
  } = registerPromise();
  const message = {
    jsonrpc: Two,
    method,
    params,
    id
  };
  return {
    message,
    promise
  };
};
class JsonRpcError extends Error {
  constructor(message) {
    super(message);
    this.name = 'JsonRpcError';
  }
}
const NewLine$2 = '\n';
const DomException = 'DOMException';
const ReferenceError$1 = 'ReferenceError';
const SyntaxError$1 = 'SyntaxError';
const TypeError$1 = 'TypeError';
const getErrorConstructor = (message, type) => {
  if (type) {
    switch (type) {
      case DomException:
        return DOMException;
      case TypeError$1:
        return TypeError;
      case SyntaxError$1:
        return SyntaxError;
      case ReferenceError$1:
        return ReferenceError;
      default:
        return Error;
    }
  }
  if (message.startsWith('TypeError: ')) {
    return TypeError;
  }
  if (message.startsWith('SyntaxError: ')) {
    return SyntaxError;
  }
  if (message.startsWith('ReferenceError: ')) {
    return ReferenceError;
  }
  return Error;
};
const constructError = (message, type, name) => {
  const ErrorConstructor = getErrorConstructor(message, type);
  if (ErrorConstructor === DOMException && name) {
    return new ErrorConstructor(message, name);
  }
  if (ErrorConstructor === Error) {
    const error = new Error(message);
    if (name && name !== 'VError') {
      error.name = name;
    }
    return error;
  }
  return new ErrorConstructor(message);
};
const getNewLineIndex$1 = (string, startIndex = undefined) => {
  return string.indexOf(NewLine$2, startIndex);
};
const getParentStack = error => {
  let parentStack = error.stack || error.data || error.message || '';
  if (parentStack.startsWith('    at')) {
    parentStack = error.message + NewLine$2 + parentStack;
  }
  return parentStack;
};
const joinLines$1 = lines => {
  return lines.join(NewLine$2);
};
const MethodNotFound = -32601;
const Custom = -32001;
const splitLines$1 = lines => {
  return lines.split(NewLine$2);
};
const restoreJsonRpcError = error => {
  if (error && error instanceof Error) {
    return error;
  }
  const currentStack = joinLines$1(splitLines$1(new Error().stack || '').slice(1));
  if (error && error.code && error.code === MethodNotFound) {
    const restoredError = new JsonRpcError(error.message);
    const parentStack = getParentStack(error);
    restoredError.stack = parentStack + NewLine$2 + currentStack;
    return restoredError;
  }
  if (error && error.message) {
    const restoredError = constructError(error.message, error.type, error.name);
    if (error.data) {
      if (error.data.stack && error.data.type && error.message) {
        restoredError.stack = error.data.type + ': ' + error.message + NewLine$2 + error.data.stack + NewLine$2 + currentStack;
      } else if (error.data.stack) {
        restoredError.stack = error.data.stack;
      }
      if (error.data.codeFrame) {
        // @ts-ignore
        restoredError.codeFrame = error.data.codeFrame;
      }
      if (error.data.code) {
        // @ts-ignore
        restoredError.code = error.data.code;
      }
      if (error.data.type) {
        // @ts-ignore
        restoredError.name = error.data.type;
      }
    } else {
      if (error.stack) {
        const lowerStack = restoredError.stack || '';
        // @ts-ignore
        const indexNewLine = getNewLineIndex$1(lowerStack);
        const parentStack = getParentStack(error);
        // @ts-ignore
        restoredError.stack = parentStack + lowerStack.slice(indexNewLine);
      }
      if (error.codeFrame) {
        // @ts-ignore
        restoredError.codeFrame = error.codeFrame;
      }
    }
    return restoredError;
  }
  if (typeof error === 'string') {
    return new Error(`JsonRpc Error: ${error}`);
  }
  return new Error(`JsonRpc Error: ${error}`);
};
const unwrapJsonRpcResult = responseMessage => {
  if ('error' in responseMessage) {
    const restoredError = restoreJsonRpcError(responseMessage.error);
    throw restoredError;
  }
  if ('result' in responseMessage) {
    return responseMessage.result;
  }
  throw new JsonRpcError('unexpected response message');
};
const warn = (...args) => {
  console.warn(...args);
};
const resolve = (id, response) => {
  const fn = get$2(id);
  if (!fn) {
    console.log(response);
    warn(`callback ${id} may already be disposed`);
    return;
  }
  fn(response);
  remove(id);
};
const E_COMMAND_NOT_FOUND = 'E_COMMAND_NOT_FOUND';
const getErrorType = prettyError => {
  if (prettyError && prettyError.type) {
    return prettyError.type;
  }
  if (prettyError && prettyError.constructor && prettyError.constructor.name) {
    return prettyError.constructor.name;
  }
  return undefined;
};
const getErrorProperty = (error, prettyError) => {
  if (error && error.code === E_COMMAND_NOT_FOUND) {
    return {
      code: MethodNotFound,
      message: error.message,
      data: error.stack
    };
  }
  return {
    code: Custom,
    message: prettyError.message,
    data: {
      stack: prettyError.stack,
      codeFrame: prettyError.codeFrame,
      type: getErrorType(prettyError),
      code: prettyError.code,
      name: prettyError.name
    }
  };
};
const create$1$1 = (message, error) => {
  return {
    jsonrpc: Two,
    id: message.id,
    error
  };
};
const getErrorResponse = (message, error, preparePrettyError, logError) => {
  const prettyError = preparePrettyError(error);
  logError(error, prettyError);
  const errorProperty = getErrorProperty(error, prettyError);
  return create$1$1(message, errorProperty);
};
const create$7 = (message, result) => {
  return {
    jsonrpc: Two,
    id: message.id,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$7(message, resultProperty);
};
const getResponse = async (message, ipc, execute, preparePrettyError, logError, requiresSocket) => {
  try {
    const result = requiresSocket(message.method) ? await execute(message.method, ipc, ...message.params) : await execute(message.method, ...message.params);
    return getSuccessResponse(message, result);
  } catch (error) {
    return getErrorResponse(message, error, preparePrettyError, logError);
  }
};
const defaultPreparePrettyError = error => {
  return error;
};
const defaultLogError = () => {
  // ignore
};
const defaultRequiresSocket = () => {
  return false;
};
const defaultResolve = resolve;

// TODO maybe remove this in v6 or v7, only accept options object to simplify the code
const normalizeParams = args => {
  if (args.length === 1) {
    const options = args[0];
    return {
      ipc: options.ipc,
      message: options.message,
      execute: options.execute,
      resolve: options.resolve || defaultResolve,
      preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
      logError: options.logError || defaultLogError,
      requiresSocket: options.requiresSocket || defaultRequiresSocket
    };
  }
  return {
    ipc: args[0],
    message: args[1],
    execute: args[2],
    resolve: args[3],
    preparePrettyError: args[4],
    logError: args[5],
    requiresSocket: args[6]
  };
};
const handleJsonRpcMessage = async (...args) => {
  const options = normalizeParams(args);
  const {
    message,
    ipc,
    execute,
    resolve,
    preparePrettyError,
    logError,
    requiresSocket
  } = options;
  if ('id' in message) {
    if ('method' in message) {
      const response = await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
      try {
        ipc.send(response);
      } catch (error) {
        const errorResponse = getErrorResponse(message, error, preparePrettyError, logError);
        ipc.send(errorResponse);
      }
      return;
    }
    resolve(message.id, message);
    return;
  }
  if ('method' in message) {
    await getResponse(message, ipc, execute, preparePrettyError, logError, requiresSocket);
    return;
  }
  throw new JsonRpcError('unexpected message');
};
const invokeHelper = async (ipc, method, params, useSendAndTransfer) => {
  const {
    message,
    promise
  } = create$2$1(method, params);
  if (useSendAndTransfer && ipc.sendAndTransfer) {
    ipc.sendAndTransfer(message);
  } else {
    ipc.send(message);
  }
  const responseMessage = await promise;
  return unwrapJsonRpcResult(responseMessage);
};
const send$1 = (transport, method, ...params) => {
  const message = create$4$1(method, params);
  transport.send(message);
};
const invoke$2 = (ipc, method, ...params) => {
  return invokeHelper(ipc, method, params, false);
};
const invokeAndTransfer$1 = (ipc, method, ...params) => {
  return invokeHelper(ipc, method, params, true);
};

const state$2 = {
  /**
   * @type {any}
   */
  ipc: undefined
};
const get$1 = () => {
  return state$2.ipc;
};
const set$1 = ipc => {
  state$2.ipc = ipc;
};

const invoke$1 = (method, ...params) => {
  const ipc = get$1();
  return invoke$2(ipc, method, ...params);
};
const invokeAndTransfer = async (method, ...params) => {
  const ipc = get$1();
  return invokeAndTransfer$1(ipc, method, ...params);
};
const listen$3 = ipc => {
  set$1(ipc);
};

const create$6 = async canvasId => {
  number(canvasId);
  const {
    id,
    promise
  } = registerPromise();
  await invoke$1('OffscreenCanvas.createForTerminal', canvasId, id);
  const response = await promise;
  const canvas = response;
  return canvas;
};
const handleResult = (id, result) => {
  resolve(id, result);
};

const BACKGROUND = "#c00ccc";
const CHAR_WIDTH$2 = 12;
const CHAR_HEIGHT$2 = 15;

/**
 *
 * @param {HTMLCanvasElement} canvas
 * @returns
 */
const createDrawCursor = canvas => {
  const ctx = canvas.getContext("2d");
  let previousX = -1;
  let previousY = -1;
  let previousCursorStyle = -1;
  const drawCursor = (x, y, cursorVisible, cursorStyle) => {
    if (cursorVisible) {
      if (previousX === x && previousY === y && previousCursorStyle === cursorStyle) {
        return;
      }
      ctx.clearRect(previousX * CHAR_WIDTH$2, previousY * CHAR_HEIGHT$2, CHAR_WIDTH$2, CHAR_HEIGHT$2);
      switch (cursorStyle) {
        case /* bar */1:
          break;
        case /* block */2:
          ctx.fillStyle = BACKGROUND;
          ctx.fillRect(x * CHAR_WIDTH$2, y * CHAR_HEIGHT$2, CHAR_WIDTH$2, CHAR_HEIGHT$2);
          break;
        case /* underline */3:
          break;
        case /* blur */4:
          ctx.strokeStyle = BACKGROUND;
          ctx.strokeRect(x * CHAR_WIDTH$2, y * CHAR_HEIGHT$2, CHAR_WIDTH$2, CHAR_HEIGHT$2);
          break;
      }
      previousX = x;
      previousY = y;
    } else {
      ctx.clearRect(previousX * CHAR_WIDTH$2, previousY * CHAR_HEIGHT$2, CHAR_WIDTH$2, CHAR_HEIGHT$2);
    }
  };
  return drawCursor;
};

const CHAR_WIDTH$1 = 12;
const CHAR_HEIGHT$1 = 15;

const supportsOffscreenCanvas = true;

const supportsTransferToImageBitMap = (() => {
  try {
    supportsOffscreenCanvas ? (() => {
      const canvas = new OffscreenCanvas(CHAR_WIDTH$1, CHAR_HEIGHT$1);
      canvas.getContext("2d");
      canvas.transferToImageBitmap();
    })() : (() => {
      const canvas = document.createElement("canvas");
      canvas.width = CHAR_WIDTH$1;
      canvas.height = CHAR_HEIGHT$1;
      canvas.transferToImageBitmap();
    })();
    return true;
  } catch {
    return false;
  }
})();

const tmpCanvas = new OffscreenCanvas(CHAR_WIDTH$1, CHAR_HEIGHT$1) ;
const tmpCtx = tmpCanvas.getContext("2d", {
  // desynchronized: true, // perf
  alpha: false // perf
});
const bitmapCache = Object.create(null);
const createDrawLines = (canvas, lines, bufferLines, offsets, attributes, rows, cols, defaultBackground, defaultForeground) => {
  const ctx = canvas.getContext("2d", {
    // desynchronized: true, // perf
    alpha: false // perf
  });
  ctx.fillStyle = defaultBackground;
  const drawChar = (char, x, y, background, foreground) => {
    if (char === " " && background === defaultBackground) {
      return;
    }
    const cacheKey = `${char}${background}${foreground}`;
    if (!(cacheKey in bitmapCache)) {
      tmpCtx.fillStyle = background;
      tmpCtx.fillRect(0, 0, CHAR_WIDTH$1, CHAR_HEIGHT$1);
      tmpCtx.font = `${CHAR_HEIGHT$1}px monospace`;
      tmpCtx.fillStyle = foreground;
      tmpCtx.fillText(char, 0, CHAR_HEIGHT$1);
      bitmapCache[cacheKey] = supportsTransferToImageBitMap ? tmpCanvas.transferToImageBitmap() : tmpCtx.getImageData(0, 0, CHAR_WIDTH$1, CHAR_HEIGHT$1);
    }
    supportsTransferToImageBitMap ? ctx.drawImage(bitmapCache[cacheKey], x * CHAR_WIDTH$1, y * CHAR_HEIGHT$1) : ctx.putImageData(bitmapCache[cacheKey], x * CHAR_WIDTH$1, y * CHAR_HEIGHT$1);
  };
  const getChars = y => {
    const text = new TextDecoder().decode(lines[y].subarray(0, offsets[y]));
    const chars = [...text];
    return chars;
  };
  const drawLine = (bufferY, positionY) => {
    let x = -1;
    const chars = getChars(bufferY);
    // const chars = lines[y]
    const attributesOnLine = attributes[bufferY] || {};
    let currentAttributes = {
      foreground: defaultForeground,
      background: defaultBackground
    };
    while (++x < chars.length) {
      currentAttributes = attributesOnLine[x] || currentAttributes;
      // const { char, background, foreground } = chars[x]
      const char = chars[x];
      const background = currentAttributes.background || defaultBackground;
      const foreground = currentAttributes.foreground || defaultForeground;
      drawChar(char, x, positionY, background, foreground);
    }
  };
  const clearLines = (x, y, width, height) => {
    // ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
    ctx.fillRect(x * CHAR_WIDTH$1, y * CHAR_HEIGHT$1, width * CHAR_WIDTH$1, height * CHAR_HEIGHT$1);
  };
  const drawLines = (start, end, bufferYEnd) => {
    // // console.log('draw all lines')
    // console.log({ start, end })
    // console.log({ bufferYEnd })
    clearLines(0, start, cols, end - start);
    if (bufferYEnd < rows) {
      for (let i = 0; i < rows - bufferYEnd; i++) {
        drawLine(bufferLines - i - 1, rows - i);
      }
      for (let i = 0; i < bufferYEnd + 1; i++) {
        drawLine(i, rows - (bufferYEnd - i));
      }
    } else {
      for (let i = 0; i < rows; i++) {
        drawLine(bufferYEnd - i, rows - i);
      }
    }
  };
  return drawLines;
};

const TopLevelContent = 1;
const Escaped = 2;
const Csi = 3;
const AfterEscape3 = 4;
const Charset = 5;
const AfterEscape3AfterSemicolon = 6;
const Osc = 8;
const Dcs = 9;
const AfterQuestionMark = 10;
const AfterQuestionMark2 = 11;
const AfterExclamationMark = 12;
const AfterSpace = 14;
const Osc2 = 16;
const Osc3 = 17;

const parseArray = (array, {
  eraseInDisplay,
  eraseToEndOfLine,
  goToHome,
  setCharAttributes,
  bell,
  cursorUp,
  cursorDown,
  cursorRight,
  cursorLeft,
  backspace,
  print,
  setGLevel,
  saveCursor,
  restoreCursor,
  index,
  tabSet,
  reverseIndex,
  keypadApplicationMode,
  keypadNumericMode,
  fullReset,
  nextLine,
  carriageReturn,
  deleteCharacters,
  setWindowTitle,
  cursorPosition,
  softTerminalReset,
  cursorNextLine,
  cursorPrecedingLine,
  cursorCharacterAbsolute,
  cursorForwardTabulation,
  cursorBackwardTabulation,
  insertLines,
  deleteLines,
  scrollUp,
  scrollDown,
  eraseCharacters,
  characterPositionAbsolute,
  characterPositionRelative,
  repeatPrecedingGraphicCharacter,
  sendDeviceAttributesPrimary,
  sendDeviceAttributesTertiary,
  linePositionAbsolute,
  eraseInLine,
  linePositionRelative,
  horizontalAndVerticalPosition,
  tabClear,
  setMode,
  resetMode,
  lineFeed,
  privateModeSet,
  privateModeReset,
  setCursorStyle,
  shiftLeftColumns,
  insertBlankCharacters,
  setTextParameters
}) => {
  // let state = State.TopLevelContent
  // let i = 0
  // let currentParam
  // let params = []
  // let printStartIndex = -1

  if (!(array instanceof Uint8Array)) {
    throw new Error("invalid data, must be of type Uint8Array");
  }
  let state = TopLevelContent;
  let i = 0;
  let currentParam = 0;
  let params = [];
  let printStartIndex = -1;
  while (i < array.length) {
    const value = array[i];
    switch (state) {
      case TopLevelContent:
        middle: switch (value) {
          case 1:
          case 2:
          case 3:
          case 4:
          case 5:
          case 6:
            i++;
            break;
          case /* \u0007 */7:
            bell();
            i++;
            break;
          case /* \u0008 */8:
            backspace();
            i++;
            break;
          case /* \t */9:
            i++;
            break;
          case /* \n */10:
            lineFeed();
            i++;
            break;
          case 11:
          case 12:
            i++;
            break;
          case /* \r */13:
            carriageReturn();
            i++;
            break;
          case 14:
          case 15:
          case 16:
          case 17:
          case 18:
          case 19:
          case 20:
          case 21:
          case 22:
          case 23:
          case 24:
          case 25:
          case 26:
            i++;
            break;
          case /* \u001b */27:
            state = Escaped;
            i++;
            break;
          case 28:
          case 29:
          case 30:
          case 31:
            i++;
            break;
          case 32:
          case 33:
          case 34:
          case 35:
          case 36:
          case 37:
          case 38:
          case 39:
          case 40:
          case 41:
          case 42:
          case 43:
          case 44:
          case 45:
          case 46:
          case 47:
          case 48:
          case 49:
          case 50:
          case 51:
          case 52:
          case 53:
          case 54:
          case 55:
          case 56:
          case 57:
          case 58:
          case 59:
          case 60:
          case 61:
          case 62:
          case 63:
          case 64:
          case 65:
          case 66:
          case 67:
          case 68:
          case 69:
          case 70:
          case 71:
          case 72:
          case 73:
          case 74:
          case 75:
          case 76:
          case 77:
          case 78:
          case 79:
          case 80:
          case 81:
          case 82:
          case 83:
          case 84:
          case 85:
          case 86:
          case 87:
          case 88:
          case 89:
          case 90:
          case 91:
          case 92:
          case 93:
          case 94:
          case 95:
          case 96:
          case 97:
          case 98:
          case 99:
          case 100:
          case 101:
          case 102:
          case 103:
          case 104:
          case 105:
          case 106:
          case 107:
          case 108:
          case 109:
          case 110:
          case 111:
          case 112:
          case 113:
          case 114:
          case 115:
          case 116:
          case 117:
          case 118:
          case 119:
          case 120:
          case 121:
          case 122:
          case 123:
          case 124:
          case 125:
          case 126:
          case 127:
            printStartIndex = i++;
            while (i < array.length) {
              const element = array[i];
              if (element >= 32 && element < 126) {
                i++;
                continue;
              }
              switch (element) {
                case /* \u001b */27:
                  print(array, printStartIndex, i);
                  state = Escaped;
                  i++;
                  break middle;
                case /* \u0007 */7:
                  print(array, printStartIndex, i);
                  bell();
                  i++;
                  state = TopLevelContent;
                  break middle;
                case /* \u0008 */8:
                  print(array, printStartIndex, i);
                  backspace();
                  i++;
                  state = TopLevelContent;
                  break middle;
                case /* \r */13:
                  print(array, printStartIndex, i);
                  carriageReturn();
                  i++;
                  state = TopLevelContent;
                  break middle;
                default:
                  i++;
                  break;
              }
            }
            print(array, printStartIndex, i);
            break;
          default:
            // TODO this is slow
            throw new Error("no");
        }
        break;
      case Escaped:
        switch (value) {
          case /* ( */40:
            state = Charset;
            break;
          case /* [ */91:
            params = [];
            state = Csi;
            break;
          case /* ] */93:
            state = Osc;
            break;
          case /* P */80:
            state = Dcs;
            break;
          case /* _ */95:
            break;
          case /* ^ */94:
            break;
          case /* c */99:
            break;
          case /* E */69:
          case /* D */68:
            break;
          case /* 7 */55:
            state = TopLevelContent;
            break;
          case /* 8 */56:
            backspace();
            state = TopLevelContent;
            break;
          case /* H */72:
            break;
          default:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case Charset:
        i++;
        break;
      case Csi:
        switch (value) {
          case /*   */32:
            state = AfterSpace;
            break;
          case /* ! */33:
            state = AfterExclamationMark;
            break;
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = value - 48;
            state = AfterEscape3;
            break;
          case /* ? */63:
            state = AfterQuestionMark;
            break;
          case /* @ */64:
            state = TopLevelContent;
            break;
          case /* A */65:
            cursorUp(params);
            state = TopLevelContent;
            break;
          case /* B */66:
            state = TopLevelContent;
            break;
          case /* C */67:
            cursorRight(params);
            state = TopLevelContent;
            break;
          case /* D */68:
            cursorLeft(params);
            state = TopLevelContent;
            break;
          case /* E */69:
            state = TopLevelContent;
            break;
          case /* F */70:
            state = TopLevelContent;
            break;
          case /* G */71:
            state = TopLevelContent;
            break;
          case /* H */72:
            cursorPosition(params);
            state = TopLevelContent;
            break;
          case /* I */73:
            state = TopLevelContent;
            break;
          case /* J */74:
            eraseInDisplay(params);
            state = TopLevelContent;
            break;
          case /* K */75:
            eraseInLine(params);
            state = TopLevelContent;
            break;
          case /* L */76:
            state = TopLevelContent;
            break;
          case /* M */77:
            state = TopLevelContent;
            break;
          case /* P */80:
            state = TopLevelContent;
            break;
          case /* S */83:
            state = TopLevelContent;
            break;
          case /* T */84:
            state = TopLevelContent;
            break;
          case /* X */88:
            state = TopLevelContent;
            break;
          case /* Z */90:
            state = TopLevelContent;
            break;
          case /* ^ */94:
            state = TopLevelContent;
            break;
          case /* ` */96:
            state = TopLevelContent;
            break;
          case /* a */97:
            state = TopLevelContent;
            break;
          case /* b */98:
            state = TopLevelContent;
            break;
          case /* d */100:
            state = TopLevelContent;
            break;
          case /* e */101:
            state = TopLevelContent;
            break;
          case /* f */102:
            state = TopLevelContent;
            break;
          case /* g */103:
            state = TopLevelContent;
            break;
          case /* h */104:
            state = TopLevelContent;
            break;
          case /* l */108:
            state = TopLevelContent;
            break;
          case /* m */109:
            setCharAttributes(params);
            state = TopLevelContent;
            break;
          case /* u */117:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case AfterEscape3:
        switch (value) {
          case /*   */32:
            params.push(currentParam);
            state = AfterSpace;
            break;
          case /* ; */59:
            params.push(currentParam);
            state = AfterEscape3AfterSemicolon;
            break;
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = currentParam * 10 + value - 48;
            break;
          case /* @ */64:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* A */65:
            params.push(currentParam);
            cursorUp(params);
            state = TopLevelContent;
            break;
          case /* B */66:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* C */67:
            params.push(currentParam);
            cursorRight(params);
            state = TopLevelContent;
            break;
          case /* D */68:
            params.push(currentParam);
            cursorLeft(params);
            state = TopLevelContent;
            break;
          case /* E */69:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* F */70:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* G */71:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* H */72:
            params.push(currentParam);
            cursorPosition(params);
            state = TopLevelContent;
            break;
          case /* I */73:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* J */74:
            params.push(currentParam);
            eraseInDisplay(params);
            state = TopLevelContent;
            break;
          case /* K */75:
            params.push(currentParam);
            eraseInLine(params);
            state = TopLevelContent;
            break;
          case /* L */76:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* M */77:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* P */80:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* S */83:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* T */84:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* X */88:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* Z */90:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* ^ */94:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* ` */96:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* a */97:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* b */98:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* d */100:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* e */101:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* f */102:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* g */103:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* h */104:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* l */108:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* m */109:
            params.push(currentParam);
            setCharAttributes(params);
            state = TopLevelContent;
            break;
          default:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case AfterEscape3AfterSemicolon:
        switch (value) {
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = value - 48;
            state = AfterEscape3;
            break;
        }
        i++;
        break;
      case AfterQuestionMark:
        switch (value) {
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = value - 48;
            state = AfterQuestionMark2;
            break;
          case /* J */74:
            eraseInDisplay(params);
            state = TopLevelContent;
            break;
          case /* K */75:
            eraseInLine(params);
            state = TopLevelContent;
            break;
          case /* h */104:
            state = TopLevelContent;
            break;
          case /* l */108:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case AfterQuestionMark2:
        switch (value) {
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = currentParam * 10 + value - 48;
            break;
          case /* ; */59:
            params.push(currentParam);
            state = AfterQuestionMark;
            break;
          case /* J */74:
            params.push(currentParam);
            eraseInDisplay(params);
            state = TopLevelContent;
            break;
          case /* K */75:
            params.push(currentParam);
            eraseInLine(params);
            state = TopLevelContent;
            break;
          case /* h */104:
            params.push(currentParam);
            state = TopLevelContent;
            break;
          case /* l */108:
            params.push(currentParam);
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case AfterExclamationMark:
        switch (value) {
          case /* p */112:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case AfterSpace:
        switch (value) {
          case /* @ */64:
            state = TopLevelContent;
            break;
          case /* q */113:
            state = TopLevelContent;
            break;
          case /* t */116:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case Osc:
        switch (value) {
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = value - 48;
            state = Osc2;
            break;
          case /* \u0007 */7:
            state = TopLevelContent;
            break;
        }
        i++;
        break;
      case Osc2:
        switch (value) {
          case /* 0 */48:
          case /* 1 */49:
          case /* 2 */50:
          case /* 3 */51:
          case /* 4 */52:
          case /* 5 */53:
          case /* 6 */54:
          case /* 7 */55:
          case /* 8 */56:
          case /* 9 */57:
            currentParam = currentParam * 10 + value - 48;
            break;
          case /* ; */59:
            params.push(currentParam);
            state = Osc3;
            break;
        }
        i++;
        break;
      case Osc3:
        switch (value) {
          case /* \u0007 */7:
            state = TopLevelContent;
            break;
          default:
            params.push(value);
        }
        i++;
    }
  }
};

// const demo = () => {
//   const input = `[K[0;35msimon[0;32m (master *)[0;34m playground $ [0m`;
//   createParse({
//     cursorDown: () => console.log("cursor down"),
//     cursorNextLine: () => console.log("cursor next line"),
//     cursorCharacterAbsolute: () => console.log("cursor character absolute"),
//     setCharAttributes: (params) => console.log("set char attributes", params),
//     privateModeSet: (params) => console.log("private mode set", params),
//     setMode: (params) => console.log("setMode", params),
//     softTerminalReset: () => console.log("soft terminal reset"),
//     setCursorStyle: (params) => console.log("set cursor style", params),
//     restoreCursor: (params) => console.log("restore cursor", params),
//     shiftLeftColumns: (params) => console.log("shiftLeftColumns", params),
//     print: (params) => console.log("print", params),
//     lineFeed: () => console.log("lineFeed"),
//     bell: () => console.log("bell"),
//     setTextParameters: (params) => console.log("set text parameters", params),
//     carriageReturn: () => [],
//     cursorPosition: () => {},
//     eraseInDisplay: () => {},
//     eraseInLine: () => {},
//   })(new Uint8Array(Buffer.from(input, "utf-8")));
// };

// demo();

const transformKey = event => {
  const modifiers = (event.shiftKey ? 1 : 0) | (event.altKey ? 2 : 0) | (event.ctrlKey ? 4 : 0) | (event.metaKey ? 8 : 0);
  switch (event.key) {
    case "A":
    case "B":
    case "C":
    case "D":
    case "E":
    case "F":
    case "G":
    case "H":
    case "I":
    case "J":
    case "K":
    case "L":
    case "M":
    case "N":
    case "O":
    case "P":
    case "Q":
    case "R":
    case "S":
    case "T":
    case "U":
    case "V":
    case "W":
    case "X":
    case "Y":
    case "Z":
      if (event.ctrlKey) {
        const char = String.fromCharCode(event.key.charCodeAt() - 64);
        if (event.altKey) {
          return `\u001b${char}`;
        }
        return char;
      }
      return event.key;
    case "a":
    case "b":
    case "c":
    case "d":
    case "e":
    case "f":
    case "g":
    case "h":
    case "i":
    case "j":
    case "k":
    case "l":
    case "m":
    case "n":
    case "o":
    case "p":
    case "q":
    case "r":
    case "s":
    case "t":
    case "u":
    case "v":
    case "w":
    case "x":
    case "y":
    case "z":
      if (event.ctrlKey) {
        // if (event.key === 'r' || event.key === 'l') {
        //   event.preventDefault()
        // }
        const char = String.fromCharCode(event.key.charCodeAt() - 96);
        if (event.altKey) {
          return `\u001b${char}`;
        }
        return char;
      }
      return event.key;
    case "F1":
      return "\u001bOP";
    case "F2":
      return "\u001bOQ";
    case "F3":
      return "\u001bOR";
    case "F4":
      return "\u001bOS";
    case "F5":
      return "\u001b[15~";
    case "F6":
      return "\u001b[17~";
    case "F7":
      return "\u001b[18~";
    case "F8":
      return "\u001b[19~";
    case "F9":
      return "\u001b[20~";
    case "F10":
      return "\u001b[21~";
    case "F11":
      return "\u001b[23~";
    case "F12":
      return "\u001b[24~";
    case "Home":
      if (modifiers) {
        return `\u001b[1;${modifiers + 1}H`;
      }
      return `\u001b[H`;
    case "End":
      if (modifiers) {
        return `\u001b[1;${modifiers + 1}F`;
      }
      return `\u001b[F`;
    case "Insert":
      if (event.shiftKey || event.ctrlKey) {
        return "";
      }
      return "\u001b[2~";
    case "Delete":
      if (modifiers) {
        return `\u001b[3;${modifiers + 1}~`;
      }
      return "\u001b[3~";
    case "ArrowUp":
      // event.preventDefault()
      if (modifiers) {
        return `\u001b[1;${modifiers + 1}A`;
      }
      return "\u001b[A";
    case "ArrowDown":
      // event.preventDefault()
      if (modifiers) {
        return `\u001b[1;${modifiers + 1}B`;
      }
      return "\u001b[B";
    case "ArrowRight":
      // event.preventDefault()
      if (modifiers) {
        return `\u001b[1;${modifiers + 1}C`;
      }
      return "\u001b[C";
    case "ArrowLeft":
      // event.preventDefault()
      if (modifiers) {
        return `\u001b[1;${modifiers + 1}D`;
      }
      return "\u001b[D";
    case "UIKeyInputUpArrow":
      return "\u001b[A";
    case "UIKeyInputDownArrow":
      return "\u001b[B";
    case "UIKeyInputRightArrow":
      return "\u001b[C";
    case "UIKeyInputLeftArrow":
      return "\u001b[D";
    case "Enter":
      if (event.altKey) {
        return "\u001b\r";
      }
      return "\n";
    case "PageUp":
      if (event.ctrlKey) {
        return `\u001b[5;5~`;
      }
      return "\u001b[5~";
    case "PageDown":
      if (event.ctrlKey) {
        return `\u001b[6;5~`;
      }
      return `\u001b[6~`;
    case "Backspace":
      if (event.altKey) {
        return "\x17";
      }
      if (event.shiftKey) {
        return "\u0008";
      }
      return "\x7f";
    case "Tab":
      // event.preventDefault()
      if (event.shiftKey) {
        return "\u001b[Z";
      }
      return "\t";
    case "Escape":
      if (event.altKey) {
        return "\u001b\u001b";
      }
      return "\u001b";
    case "/":
      // event.preventDefault()
      return "/";
    case "?":
      if (event.ctrlKey) {
        return "\u007f";
      }
      if (event.altKey) {
        return `\u001b?`;
      }
      return event.key;
    case "|":
      if (event.ctrlKey) {
        return "\u001c";
      }
      if (event.altKey) {
        return `\u001b|`;
      }
      return event.key;
    case "{":
      if (event.ctrlKey) {
        return "\u001b";
      }
      if (event.altKey) {
        return `\u001b{`;
      }
      return event.key;
    case "}":
      if (event.ctrlKey) {
        return "\u001d";
      }
      if (event.altKey) {
        return `\u001b}`;
      }
      return event.key;
    case "_":
      if (event.ctrlKey) {
        return "\u001f";
      }
      if (event.altKey) {
        return `\u001b_`;
      }
      return event.key;
    case "^":
      if (event.ctrlKey) {
        return "\u001e";
      }
      if (event.altKey) {
        return `\u001b^`;
      }
      return event.key;
    case "@":
      if (event.ctrlKey) {
        return "\u0000";
      }
      if (event.altKey) {
        return `\u001b@`;
      }
      return event.key;
    case " ":
      // event.preventDefault()
      return ` `;
    default:
      if (event.key.length > 1) {
        return "";
      }
      if (event.altKey) {
        return `\u001b${event.key}`;
      }
      return event.key;
  }
};

const CHAR_WIDTH = 13;
const CHAR_HEIGHT = 15;
const COLS = 80;
const ROWS = 25;
const BUFFER_LINES = 200;
const noop = () => {};
const createOffscreenTerminal = ({
  background = "#000000",
  foreground = "#ffffff",
  handleInput,
  bell = noop,
  setWindowTitle = noop,
  handleFocus = noop,
  canvasText,
  canvasCursor,
  focusTextArea = noop
}) => {
  let focused = false;
  const handleKeyDown = event => {
    const transformedKey = transformKey(event);
    if (transformedKey) {
      handleInput(transformedKey);
    }
  };
  const handleBlur = () => {
    focused = false;
    cursorStyle = /* blur */4;
    requestAnimationFrame(render);
  };
  const focus = () => {
    handleFocus();
    if (focused) {
      return;
    }
    focused = true;
    cursorStyle = /* block */2;
    focusTextArea();
    requestAnimationFrame(render);
  };
  const WIDTH = COLS * CHAR_WIDTH;
  const HEIGHT = ROWS * (CHAR_HEIGHT + 10);
  canvasText.width = canvasCursor.width = WIDTH;
  canvasText.height = canvasCursor.height = HEIGHT;
  let bufferYEnd = ROWS;
  let cursorYRelative = -25;
  let cursorXRelative = -80;
  let cellForeground = "#ffffff";
  let cellBackground = "#000000";
  let cursorVisible = true;
  let cursorStyle = /* block */2;
  const dirty = {
    start: 0,
    end: 0
  };

  // self.stats = () => ({
  //   cursorYRelative,
  //   cursorXRelative,
  //   x: COLS + cursorXRelative,
  //   y: bufferYEnd + cursorYRelative,
  // });

  const lines = [];
  // self.lines = lines;

  const offsets = new Uint8Array(BUFFER_LINES);

  // self.offsets = offsets;
  let attributes = {};

  // self.attributes = attributes;

  for (let y = 0; y < BUFFER_LINES; y++) {
    lines.push(new Uint8Array(300));
  }
  new TextDecoder();

  // self.printLines = () => {
  //   console.log(
  //     lines.map((line, y) => textDecoder.decode(line.subarray(0, offsets[y])))
  //   );
  // };

  const dirtyMark = y => {
    // console.log('dirty' + y)
    if (y < dirty.start) {
      dirty.start = y;
    } else if (y > dirty.end) {
      dirty.end = y;
    }
  };
  const dirtyClear = () => {
    // console.log('dirty clear')
    dirty.start = dirty.end = bufferYEnd + cursorYRelative;
  };
  const callbackFns = {
    eraseInLine() {
      const y = bufferYEnd + cursorYRelative;
      const x = COLS + cursorXRelative;
      offsets[y] = x;
    },
    eraseInDisplay() {
      // console.log('erase in display')
      offsets.fill(0);
      cursorYRelative = -25 + 1;
      cursorXRelative = -80;
      bufferYEnd = ROWS;
      for (const key of Object.keys(attributes)) {
        delete attributes[key];
      }
      dirtyMark(0);
      dirtyMark(ROWS);
    },
    setCharAttributes(params) {
      if (params[1] === 7) {
        [cellForeground, cellBackground] = [cellBackground, cellForeground];
      } else if (params[1] === 35) {
        cellForeground = "#8000ff";
      } else if (params[1] === 32) {
        cellForeground = "#09f900";
      } else if (params[1] === 34) {
        cellForeground = "#0090ff";
      } else {
        cellForeground = foreground;
        cellBackground = background;
      }
      const y = bufferYEnd + cursorYRelative;
      attributes[y] = attributes[y] || {};
      attributes[y][offsets[y]] = {
        foreground: cellForeground,
        background: cellBackground
      };
    },
    cursorUp() {
      console.log("cursor up");
    },
    cursorDown() {
      //       console.log('cursor down')
    },
    cursorRight() {
      cursorXRelative++;
    },
    cursorLeft() {
      console.log("cursor left");
    },
    backspace() {
      cursorXRelative--;
      // offsets[bufferYEnd + cursorYRelative]--
    },
    deleteChars(numberOfChars) {
      const y = bufferYEnd + cursorYRelative;
      // offsets[y] = x - 1
      // cursorXRelative--
      const x = COLS + cursorXRelative;
      offsets[y] = x;
    },
    bell,
    print(array, start, end) {
      const subArray = array.subarray(start, end);
      const y = bufferYEnd + cursorYRelative;
      const x = COLS + cursorXRelative;
      lines[y].set(subArray, x);
      cursorXRelative += end - start;
      offsets[y] = COLS + cursorXRelative;
      dirtyMark(y);
      if (x >= COLS - 1) {
        // cursorYRelative++;
        // console.log("wrap");
        callbackFns.lineFeed();
      }
      // console.log("PRINT", { y, x, COLS, cursorXRelative, cursorYRelative });
      // self.printLines();
      // offsets[y] += end - start
      // cursorXRelative = -COLS + offsets[y]
    },
    lineFeed() {
      if (cursorYRelative === 0) {
        bufferYEnd = (bufferYEnd + 1) % BUFFER_LINES;
        offsets[bufferYEnd] = 0;
        delete attributes[bufferYEnd];
      } else {
        cursorYRelative++;
      }
      cellForeground = foreground;
      cellBackground = background;
    },
    carriageReturn() {
      cursorXRelative = -80;
    },
    setWindowTitle,
    cursorPosition(params) {
      if (params.length === 2) {
        const row = params[0];
        const column = params[1];
        cursorYRelative = -25 + row;
        cursorXRelative = -80 + column;
      }
    },
    cursorShow() {
      cursorVisible = true;
    },
    cursorHide() {
      cursorVisible = false;
    },
    insertLines() {},
    deleteLines() {},
    setTextParameters() {},
    privateModeSet() {},
    privateModeReset() {},
    eraseToEndOfLine() {},
    goToHome() {},
    setGLevel() {},
    saveCursor() {},
    restoreCursor() {},
    index() {},
    tabSet() {},
    reverseIndex() {},
    keypadApplicationMode() {},
    keypadNumericMode() {},
    fullReset() {},
    nextLine() {},
    deleteCharacters() {},
    softTerminalReset() {},
    cursorNextLine() {},
    cursorPrecedingLine() {},
    cursorCharacterAbsolute() {},
    cursorForwardTabulation() {},
    cursorBackwardTabulation() {},
    scrollUp() {},
    scrollDown() {},
    eraseCharacters() {},
    characterPositionAbsolute() {},
    characterPositionRelative() {},
    repeatPrecedingGraphicCharacter() {},
    sendDeviceAttributesPrimary() {},
    sendDeviceAttributesTertiary() {},
    linePositionAbsolute() {},
    linePositionRelative() {},
    horizontalAndVerticalPosition() {},
    tabClear() {},
    setMode() {},
    resetMode() {},
    setCursorStyle() {},
    shiftLeftColumns() {},
    insertBlankCharacters() {}
  };

  // const parse = createParse(callbackFns);
  const drawLines = createDrawLines(canvasText, lines, BUFFER_LINES, offsets, attributes, ROWS, COLS, background, foreground);
  const drawCursor = createDrawCursor(canvasCursor);
  let scheduled = false;

  // self.drawLines = () => drawLines(dirty.start, dirty.end + 1);

  const render = () => {
    // console.log(dirty)
    drawLines(dirty.start, dirty.end + 1, bufferYEnd);
    const y = ROWS + cursorYRelative;
    const x = COLS + cursorXRelative;
    // TODO without closure
    drawCursor(x, y, cursorVisible, cursorStyle);
    scheduled = false;
    dirtyClear();
  };
  const write = array => {
    parseArray(array, callbackFns);
    // if (lines.length > 1_000) {
    //   lines.length = 1_000
    // }
    if (!scheduled) {
      scheduled = true;
      requestAnimationFrame(render);
    }
  };
  const pasteText = text => {
    // TODO bug first line not written
    const fixedText = "\n" + text.replaceAll("\n", "\r\n");
    const array = new TextEncoder().encode(fixedText);
    write(array);
  };
  return {
    write,
    focus,
    pasteText,
    writeText: pasteText,
    // TODO should this be exposed (only used for testing) maybe with _ prefix (internal)
    lines,
    handleKeyDown,
    handleBlur,
    handleMouseDown: focus
  };
};

const create$5 = async ({
  offscreenCanvasCursor,
  offscreenCanvasText,
  focusTextArea,
  handleInput
}) => {
  object(offscreenCanvasCursor);
  object(offscreenCanvasText);
  const terminal = createOffscreenTerminal({
    canvasCursor: offscreenCanvasCursor,
    canvasText: offscreenCanvasText,
    focusTextArea,
    handleInput
  });
  return terminal;
};

const state$1 = {
  terminals: Object.create(null)
};
const get = id => {
  return state$1.terminals[id];
};
const set = (id, terminal) => {
  state$1.terminals[id] = terminal;
};

const requiresSocket = () => {
  return false;
};
const preparePrettyError = error => {
  return error;
};
const logError = error => {
  console.error(error);
};
const handleMessage$1 = event => {
  return handleJsonRpcMessage(event.target, event.data, execute, resolve, preparePrettyError, logError, requiresSocket);
};

const handleIpc = ipc => {
  if ('addEventListener' in ipc) {
    ipc.addEventListener('message', handleMessage$1);
  } else {
    ipc.onmessage = handleMessage$1;
  }
};

const NodeAlternate = 8;

const getData = event => {
  return event.data;
};

const getPortTuple = () => {
  const {
    port1,
    port2
  } = new MessageChannel();
  return {
    port1,
    port2
  };
};

const sendMessagePortToElectron = async (port, initialCommand) => {
  await invokeAndTransfer('SendMessagePortToElectron.sendMessagePortToElectron', port, initialCommand);
};

const create$4 = async options => {
  const {
    port1,
    port2
  } = getPortTuple();
  await sendMessagePortToElectron(port2, options.initialCommand);
  return port1;
};

const Open = 1;
const Close = 2;
const Error$1 = 3;

const withResolvers$1 = () => {
  let _resolve;
  const promise = new Promise(resolve => {
    _resolve = resolve;
  });
  return {
    resolve: _resolve,
    promise
  };
};

const getFirstEvent = (eventTarget, eventMap) => {
  const {
    resolve,
    promise
  } = withResolvers$1();
  const listenerMap = Object.create(null);
  const cleanup = value => {
    for (const event of Object.keys(eventMap)) {
      eventTarget.removeEventListener(event, listenerMap[event]);
    }
    resolve(value);
  };
  for (const [event, type] of Object.entries(eventMap)) {
    const listener = event => {
      cleanup({
        type,
        event
      });
    };
    eventTarget.addEventListener(event, listener);
    listenerMap[event] = listener;
  }
  return promise;
};

const waitForWebSocketToBeOpen = webSocket => {
  return getFirstEvent(webSocket, {
    open: Open,
    close: Close,
    error: Error$1
  });
};

const Https = 'https:';
const Ws = 'ws:';
const Wss = 'wss:';

const getWebSocketProtocol = () => {
  return location.protocol === Https ? Wss : Ws;
};

const getWebSocketUrl = type => {
  const wsProtocol = getWebSocketProtocol();
  return `${wsProtocol}//${location.host}/websocket/${type}`;
};

let IpcError$1 = class IpcError extends Error {
  constructor(message) {
    super(message);
    this.name = 'IpcError';
  }
};

const normalizeLine = line => {
  if (line.startsWith('Error: ')) {
    return line.slice('Error: '.length);
  }
  if (line.startsWith('VError: ')) {
    return line.slice('VError: '.length);
  }
  return line;
};
const getCombinedMessage = (error, message) => {
  const stringifiedError = normalizeLine(`${error}`);
  if (message) {
    return `${message}: ${stringifiedError}`;
  }
  return stringifiedError;
};
const NewLine$1 = '\n';
const getNewLineIndex = (string, startIndex = undefined) => {
  return string.indexOf(NewLine$1, startIndex);
};
const mergeStacks = (parent, child) => {
  if (!child) {
    return parent;
  }
  const parentNewLineIndex = getNewLineIndex(parent);
  const childNewLineIndex = getNewLineIndex(child);
  if (childNewLineIndex === -1) {
    return parent;
  }
  const parentFirstLine = parent.slice(0, parentNewLineIndex);
  const childRest = child.slice(childNewLineIndex);
  const childFirstLine = normalizeLine(child.slice(0, childNewLineIndex));
  if (parentFirstLine.includes(childFirstLine)) {
    return parentFirstLine + childRest;
  }
  return child;
};
class VError extends Error {
  constructor(error, message) {
    const combinedMessage = getCombinedMessage(error, message);
    super(combinedMessage);
    this.name = 'VError';
    if (error instanceof Error) {
      this.stack = mergeStacks(this.stack, error.stack);
    }
    if (error.codeFrame) {
      // @ts-ignore
      this.codeFrame = error.codeFrame;
    }
    if (error.code) {
      // @ts-ignore
      this.code = error.code;
    }
  }
}

const stringifyCompact = value => {
  return JSON.stringify(value);
};

const create$3 = async ({
  type
}) => {
  string(type);
  const wsUrl = getWebSocketUrl(type);
  const webSocket = new WebSocket(wsUrl);
  const firstWebSocketEvent = await waitForWebSocketToBeOpen(webSocket);
  if (firstWebSocketEvent.type === Error$1) {
    throw new IpcError$1(`WebSocket connection error`);
  }
  if (firstWebSocketEvent.type === Close) {
    throw new IpcError$1(`Websocket connection was closed`);
  }
  return webSocket;
};
const wrap$1 = webSocket => {
  return {
    /**
     * @type {any}
     */
    handleMessage: undefined,
    webSocket,
    get onmessage() {
      return this.handleMessage;
    },
    set onmessage(listener) {
      if (listener) {
        // @ts-ignore
        this.handleMessage = event => {
          // TODO why are some events not instance of message event?
          if (event instanceof MessageEvent) {
            const data = JSON.parse(event.data);
            // @ts-ignore
            listener({
              data,
              target: this
            });
          } else {
            // @ts-ignore
            listener(event);
          }
        };
      } else {
        // @ts-ignore
        this.handleMessage = null;
      }
      this.webSocket.onmessage = this.handleMessage;
    },
    send(message) {
      if (this.webSocket.readyState !== webSocket.OPEN) {
        // @ts-ignore
        throw new VError(`Failed to send message: WebSocket is not open`);
      }
      const stringifiedMessage = stringifyCompact(message);
      this.webSocket.send(stringifiedMessage);
    }
  };
};

const IpcParentWithWebSocket = {
  __proto__: null,
  create: create$3,
  wrap: wrap$1
};

const Web = 'web';
const Electron = 'electron';
const Remote = 'remote';

const getPlatform = () => {
  // @ts-ignore
  if (typeof PLATFORM !== 'undefined') {
    // @ts-ignore
    return PLATFORM;
  }
  if (typeof process !== 'undefined' && process.env.NODE_ENV === 'test') {
    return 'test';
  }
  // TODO find a better way to pass runtime environment
  if (typeof name !== 'undefined' && name.endsWith('(Electron)')) {
    return Electron;
  }
  return Remote;
};
const platform = getPlatform(); // TODO tree-shake this out in production

const create$2 = async options => {
  switch (platform) {
    case Web:
    case Remote:
      const module = IpcParentWithWebSocket;
      const rawIpc = await module.create(options);
      if (options.raw) {
        return rawIpc;
      }
      return {
        rawIpc,
        module
      };
    case Electron:
      return create$4(options);
    default:
      throw new Error('unsupported platform');
  }
};
const wrap = port => {
  if (!(port instanceof MessagePort)) {
    return port.module.wrap(port.rawIpc);
  }
  return {
    port,
    /**
     * @type {any}
     */
    listener: undefined,
    get onmessage() {
      return this.listener;
    },
    set onmessage(listener) {
      this.listener = listener;
      const wrappedListener = event => {
        const data = getData(event);
        // @ts-ignore
        listener({
          data,
          target: this
        });
      };
      this.port.onmessage = wrappedListener;
    },
    send(message) {
      this.port.postMessage(message);
    },
    sendAndTransfer(message, transfer) {
      this.port.postMessage(message, transfer);
    }
  };
};

const IpcParentWithNodeAlternate = {
  __proto__: null,
  create: create$2,
  wrap
};

const getModule$1 = method => {
  switch (method) {
    case NodeAlternate:
      return IpcParentWithNodeAlternate;
    default:
      throw new Error('unexpected ipc type');
  }
};

const create$1 = async ({
  method,
  ...options
}) => {
  const module = getModule$1(method);
  // @ts-ignore
  const rawIpc = await module.create(options);
  if (options.raw) {
    return rawIpc;
  }
  const ipc = module.wrap(rawIpc);
  return ipc;
};

const state = {
  /**
   * @type {any}
   */
  ipc: undefined};

const launchTerminalProcess = async () => {
  try {
    const ipc = await create$1({
      method: NodeAlternate,
      type: 'terminal-process',
      name: 'Terminal Process',
      initialCommand: 'HandleMessagePortForTerminalProcess.handleMessagePortForTerminalProcess'
    });
    state.ipc = ipc;
    handleIpc(ipc);
  } catch (error) {
    throw new VError(error, 'Failed to create terminal connection');
  }
};

const listen$2 = async () => {
  await launchTerminalProcess();
};
const invoke = (method, ...params) => {
  return invoke$2(state.ipc, method, ...params);
};
const send = (method, ...params) => {
  return send$1(state.ipc, method, ...params);
};

// electron sends uint8arrays directly
// websocket sends the data as an object with data property
// conpty sends the data as a string

const toUint8Array = data => {
  if (data instanceof Uint8Array) {
    return data;
  }
  if (typeof data === 'string') {
    return new TextEncoder().encode(data);
  }
  if (data.data) {
    const actualData = data.data;
    return new Uint8Array(actualData);
  }
  throw new Error('unexpected data type');
};

const create = async (canvasTextId, canvasCursorId, id, cwd, command, args) => {
  await listen$2();
  const canvasCursor = await create$6(canvasCursorId);
  const canvasText = await create$6(canvasTextId);
  const emulator = await create$5({
    offscreenCanvasCursor: canvasCursor,
    offscreenCanvasText: canvasText,
    focusTextArea() {
      // TODO
    },
    handleInput(input) {
      send('Terminal.write', id, input);
    }
  });
  set(id, emulator);
  await invoke('Terminal.create', id, cwd, command, args);
};
const handleMessage = (id, method, ...args) => {
  const emulator = get(id);
  if (method === 'handleData') {
    const data = args[0];
    const parsedData = toUint8Array(data);
    emulator.write(parsedData);
  }
};
const handleBlur = id => {
  const emulator = get(id);
  emulator.handleBlur();
};
const handleKeyDown = (id, key) => {
  const emulator = get(id);
  emulator.handleKeyDown(key);
};
const handleMouseDown = id => {
  const emulator = get(id);
  emulator.handleMouseDown();
};

const commandMap = {
  'Terminal.create': create,
  'Viewlet.send': handleMessage,
  'Terminal.handleBlur': handleBlur,
  'Terminal.handleKeyDown': handleKeyDown,
  'Terminal.handleMouseDown': handleMouseDown,
  'OffscreenCanvas.handleResult': handleResult
};

const isMessagePort = value => {
  return value && value instanceof MessagePort;
};
const isMessagePortMain = value => {
  return value && value.constructor && value.constructor.name === 'MessagePortMain';
};
const isOffscreenCanvas = value => {
  return typeof OffscreenCanvas !== 'undefined' && value instanceof OffscreenCanvas;
};
const isInstanceOf = (value, constructorName) => {
  return value?.constructor?.name === constructorName;
};
const isSocket = value => {
  return isInstanceOf(value, 'Socket');
};
const transferrables = [isMessagePort, isMessagePortMain, isOffscreenCanvas, isSocket];
const isTransferrable = value => {
  for (const fn of transferrables) {
    if (fn(value)) {
      return true;
    }
  }
  return false;
};
const walkValue = (value, transferrables, isTransferrable) => {
  if (!value) {
    return;
  }
  if (isTransferrable(value)) {
    transferrables.push(value);
    return;
  }
  if (Array.isArray(value)) {
    for (const item of value) {
      walkValue(item, transferrables, isTransferrable);
    }
    return;
  }
  if (typeof value === 'object') {
    for (const property of Object.values(value)) {
      walkValue(property, transferrables, isTransferrable);
    }
    return;
  }
};
const getTransferrables = value => {
  const transferrables = [];
  walkValue(value, transferrables, isTransferrable);
  return transferrables;
};
const attachEvents = that => {
  const handleMessage = (...args) => {
    const data = that.getData(...args);
    that.dispatchEvent(new MessageEvent('message', {
      data
    }));
  };
  that.onMessage(handleMessage);
  const handleClose = event => {
    that.dispatchEvent(new Event('close'));
  };
  that.onClose(handleClose);
};
class Ipc extends EventTarget {
  constructor(rawIpc) {
    super();
    this._rawIpc = rawIpc;
    attachEvents(this);
  }
}
const E_INCOMPATIBLE_NATIVE_MODULE = 'E_INCOMPATIBLE_NATIVE_MODULE';
const E_MODULES_NOT_SUPPORTED_IN_ELECTRON = 'E_MODULES_NOT_SUPPORTED_IN_ELECTRON';
const ERR_MODULE_NOT_FOUND = 'ERR_MODULE_NOT_FOUND';
const NewLine = '\n';
const joinLines = lines => {
  return lines.join(NewLine);
};
const RE_AT = /^\s+at/;
const RE_AT_PROMISE_INDEX = /^\s*at async Promise.all \(index \d+\)$/;
const isNormalStackLine = line => {
  return RE_AT.test(line) && !RE_AT_PROMISE_INDEX.test(line);
};
const getDetails = lines => {
  const index = lines.findIndex(isNormalStackLine);
  if (index === -1) {
    return {
      actualMessage: joinLines(lines),
      rest: []
    };
  }
  let lastIndex = index - 1;
  while (++lastIndex < lines.length) {
    if (!isNormalStackLine(lines[lastIndex])) {
      break;
    }
  }
  return {
    actualMessage: lines[index - 1],
    rest: lines.slice(index, lastIndex)
  };
};
const splitLines = lines => {
  return lines.split(NewLine);
};
const RE_MESSAGE_CODE_BLOCK_START = /^Error: The module '.*'$/;
const RE_MESSAGE_CODE_BLOCK_END = /^\s* at/;
const isMessageCodeBlockStartIndex = line => {
  return RE_MESSAGE_CODE_BLOCK_START.test(line);
};
const isMessageCodeBlockEndIndex = line => {
  return RE_MESSAGE_CODE_BLOCK_END.test(line);
};
const getMessageCodeBlock = stderr => {
  const lines = splitLines(stderr);
  const startIndex = lines.findIndex(isMessageCodeBlockStartIndex);
  const endIndex = startIndex + lines.slice(startIndex).findIndex(isMessageCodeBlockEndIndex, startIndex);
  const relevantLines = lines.slice(startIndex, endIndex);
  const relevantMessage = relevantLines.join(' ').slice('Error: '.length);
  return relevantMessage;
};
const isModuleNotFoundMessage = line => {
  return line.includes('[ERR_MODULE_NOT_FOUND]');
};
const getModuleNotFoundError = stderr => {
  const lines = splitLines(stderr);
  const messageIndex = lines.findIndex(isModuleNotFoundMessage);
  const message = lines[messageIndex];
  return {
    message,
    code: ERR_MODULE_NOT_FOUND
  };
};
const isModuleNotFoundError = stderr => {
  if (!stderr) {
    return false;
  }
  return stderr.includes('ERR_MODULE_NOT_FOUND');
};
const isModulesSyntaxError = stderr => {
  if (!stderr) {
    return false;
  }
  return stderr.includes('SyntaxError: Cannot use import statement outside a module');
};
const RE_NATIVE_MODULE_ERROR = /^innerError Error: Cannot find module '.*.node'/;
const RE_NATIVE_MODULE_ERROR_2 = /was compiled against a different Node.js version/;
const isUnhelpfulNativeModuleError = stderr => {
  return RE_NATIVE_MODULE_ERROR.test(stderr) && RE_NATIVE_MODULE_ERROR_2.test(stderr);
};
const getNativeModuleErrorMessage = stderr => {
  const message = getMessageCodeBlock(stderr);
  return {
    message: `Incompatible native node module: ${message}`,
    code: E_INCOMPATIBLE_NATIVE_MODULE
  };
};
const getModuleSyntaxError = () => {
  return {
    message: `ES Modules are not supported in electron`,
    code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON
  };
};
const getHelpfulChildProcessError = (stdout, stderr) => {
  if (isUnhelpfulNativeModuleError(stderr)) {
    return getNativeModuleErrorMessage(stderr);
  }
  if (isModulesSyntaxError(stderr)) {
    return getModuleSyntaxError();
  }
  if (isModuleNotFoundError(stderr)) {
    return getModuleNotFoundError(stderr);
  }
  const lines = splitLines(stderr);
  const {
    actualMessage,
    rest
  } = getDetails(lines);
  return {
    message: actualMessage,
    code: '',
    stack: rest
  };
};
class IpcError extends VError {
  // @ts-ignore
  constructor(betterMessage, stdout = '', stderr = '') {
    if (stdout || stderr) {
      // @ts-ignore
      const {
        message,
        code,
        stack
      } = getHelpfulChildProcessError(stdout, stderr);
      const cause = new Error(message);
      // @ts-ignore
      cause.code = code;
      cause.stack = stack;
      super(cause, betterMessage);
    } else {
      super(betterMessage);
    }
    // @ts-ignore
    this.name = 'IpcError';
    // @ts-ignore
    this.stdout = stdout;
    // @ts-ignore
    this.stderr = stderr;
  }
}
const readyMessage = 'ready';
const getData$2 = event => {
  return event.data;
};
const listen$7 = () => {
  // @ts-ignore
  if (typeof WorkerGlobalScope === 'undefined') {
    throw new TypeError('module is not in web worker scope');
  }
  return globalThis;
};
const signal$8 = global => {
  global.postMessage(readyMessage);
};
class IpcChildWithModuleWorker extends Ipc {
  getData(event) {
    return getData$2(event);
  }
  send(message) {
    // @ts-ignore
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    // @ts-ignore
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    // ignore
  }
  onClose(callback) {
    // ignore
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
}
const wrap$f = global => {
  return new IpcChildWithModuleWorker(global);
};
const IpcChildWithModuleWorker$1 = {
  __proto__: null,
  listen: listen$7,
  signal: signal$8,
  wrap: wrap$f
};
const withResolvers = () => {
  let _resolve;
  const promise = new Promise(resolve => {
    _resolve = resolve;
  });
  return {
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    resolve: _resolve,
    promise
  };
};
const waitForFirstMessage = async port => {
  const {
    resolve,
    promise
  } = withResolvers();
  port.addEventListener('message', resolve, {
    once: true
  });
  const event = await promise;
  // @ts-ignore
  return event.data;
};
const listen$6 = async () => {
  const parentIpcRaw = listen$7();
  signal$8(parentIpcRaw);
  const parentIpc = wrap$f(parentIpcRaw);
  const firstMessage = await waitForFirstMessage(parentIpc);
  if (firstMessage.method !== 'initialize') {
    throw new IpcError('unexpected first message');
  }
  const type = firstMessage.params[0];
  if (type === 'message-port') {
    parentIpc.send({
      jsonrpc: '2.0',
      id: firstMessage.id,
      result: null
    });
    parentIpc.dispose();
    const port = firstMessage.params[1];
    return port;
  }
  return globalThis;
};
class IpcChildWithModuleWorkerAndMessagePort extends Ipc {
  getData(event) {
    return getData$2(event);
  }
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    if (this._rawIpc.close) {
      this._rawIpc.close();
    }
  }
  onClose(callback) {
    // ignore
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
    this._rawIpc.start();
  }
}
const wrap$e = port => {
  return new IpcChildWithModuleWorkerAndMessagePort(port);
};
const IpcChildWithModuleWorkerAndMessagePort$1 = {
  __proto__: null,
  listen: listen$6,
  wrap: wrap$e
};

const MessagePort$1 = 1;
const ModuleWorker = 2;
const ReferencePort = 3;
const ModuleWorkerAndMessagePort = 8;
const Auto = () => {
  if (globalThis.acceptPort) {
    return MessagePort$1;
  }
  if (globalThis.acceptReferencePort) {
    return ReferencePort;
  }
  return ModuleWorkerAndMessagePort;
};

const getModule = method => {
  switch (method) {
    case ModuleWorker:
      return IpcChildWithModuleWorker$1;
    case ModuleWorkerAndMessagePort:
      return IpcChildWithModuleWorkerAndMessagePort$1;
    default:
      throw new Error('unexpected ipc type');
  }
};

const listen$1 = async ({
  method
}) => {
  const module = getModule(method);
  // @ts-ignore
  const rawIpc = await module.listen();
  // @ts-ignore
  if (module.signal) {
    // @ts-ignore
    module.signal(rawIpc);
  }
  // @ts-ignore
  const ipc = module.wrap(rawIpc);
  return ipc;
};

const listen = async () => {
  register(commandMap);
  const ipc = await listen$1({
    method: Auto()
  });
  handleIpc(ipc);
  listen$3(ipc);
};

const main = async () => {
  await listen();
};

main();
