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$2 = '\n';
const getNewLineIndex$1 = (string, startIndex = undefined) => {
  return string.indexOf(NewLine$2, startIndex);
};
const mergeStacks = (parent, child) => {
  if (!child) {
    return parent;
  }
  const parentNewLineIndex = getNewLineIndex$1(parent);
  const childNewLineIndex = getNewLineIndex$1(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 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$1 = '\n';
const joinLines$1 = lines => {
  return lines.join(NewLine$1);
};
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$1(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$1 = lines => {
  return lines.split(NewLine$1);
};
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$1(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$1(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$1(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 waitForFirstMessage = async port => {
  const {
    resolve,
    promise
  } = 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 Two = '2.0';
const create$4 = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};
const callbacks = Object.create(null);
const set$4 = (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$4(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 = '\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 joinLines = lines => {
  return lines.join(NewLine);
};
const splitLines = lines => {
  return lines.split(NewLine);
};
const getCurrentStack = () => {
  const stackLinesToSkip = 3;
  const currentStack = joinLines(splitLines(new Error().stack || '').slice(stackLinesToSkip));
  return currentStack;
};
const getNewLineIndex = (string, startIndex = undefined) => {
  return string.indexOf(NewLine, startIndex);
};
const getParentStack = error => {
  let parentStack = error.stack || error.data || error.message || '';
  if (parentStack.startsWith('    at')) {
    parentStack = error.message + NewLine + parentStack;
  }
  return parentStack;
};
const MethodNotFound = -32601;
const Custom = -32001;
const restoreJsonRpcError = error => {
  const currentStack = getCurrentStack();
  if (error && error instanceof Error) {
    if (typeof error.stack === 'string') {
      error.stack = error.stack + NewLine + currentStack;
    }
    return error;
  }
  if (error && error.code && error.code === MethodNotFound) {
    const restoredError = new JsonRpcError(error.message);
    const parentStack = getParentStack(error);
    restoredError.stack = parentStack + NewLine + 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 + error.data.stack + NewLine + 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(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 isAlreadyStack = line => {
  return line.trim().startsWith('at ');
};
const getStack = prettyError => {
  const stackString = prettyError.stack || '';
  const newLineIndex = stackString.indexOf('\n');
  if (newLineIndex !== -1 && !isAlreadyStack(stackString.slice(0, newLineIndex))) {
    return stackString.slice(newLineIndex + 1);
  }
  return stackString;
};
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: getStack(prettyError),
      codeFrame: prettyError.codeFrame,
      type: getErrorType(prettyError),
      code: prettyError.code,
      name: prettyError.name
    }
  };
};
const create$1$1 = (id, error) => {
  return {
    jsonrpc: Two,
    id,
    error
  };
};
const getErrorResponse = (id, error, preparePrettyError, logError) => {
  const prettyError = preparePrettyError(error);
  logError(error, prettyError);
  const errorProperty = getErrorProperty(error, prettyError);
  return create$1$1(id, errorProperty);
};
const create$5 = (message, result) => {
  return {
    jsonrpc: Two,
    id: message.id,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$5(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
  return {
    jsonrpc: Two,
    id,
    error: {
      code: Custom,
      // @ts-ignore
      message: error.message,
      data: error
    }
  };
};
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) {
    if (ipc.canUseSimpleErrorResponse) {
      return getErrorResponseSimple(message.id, error);
    }
    return getErrorResponse(message.id, 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.id, 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 = (transport, method, ...params) => {
  const message = create$4(method, params);
  transport.send(message);
};
const invoke$1 = (ipc, method, ...params) => {
  return invokeHelper(ipc, method, params, false);
};
const invokeAndTransfer$2 = (ipc, method, ...params) => {
  return invokeHelper(ipc, method, params, true);
};

class CommandNotFoundError extends Error {
  constructor(command) {
    super(`Command not found ${command}`);
    this.name = 'CommandNotFoundError';
  }
}
const commands = Object.create(null);
const register$1 = commandMap => {
  Object.assign(commands, commandMap);
};
const getCommand = key => {
  return commands[key];
};
const execute = (command, ...args) => {
  const fn = getCommand(command);
  if (!fn) {
    throw new CommandNotFoundError(command);
  }
  return fn(...args);
};

const createRpc = ipc => {
  const rpc = {
    // @ts-ignore
    ipc,
    /**
     * @deprecated
     */
    send(method, ...params) {
      send(ipc, method, ...params);
    },
    invoke(method, ...params) {
      return invoke$1(ipc, method, ...params);
    },
    invokeAndTransfer(method, ...params) {
      return invokeAndTransfer$2(ipc, method, ...params);
    },
    async dispose() {
      await ipc?.dispose();
    }
  };
  return rpc;
};
const requiresSocket = () => {
  return false;
};
const preparePrettyError = error => {
  return error;
};
const logError = () => {
  // handled by renderer worker
};
const handleMessage$1 = event => {
  const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
  const actualExecute = event?.target?.execute || execute;
  return handleJsonRpcMessage(event.target, event.data, actualExecute, resolve, preparePrettyError, logError, actualRequiresSocket);
};
const handleIpc = ipc => {
  if ('addEventListener' in ipc) {
    ipc.addEventListener('message', handleMessage$1);
  } else if ('on' in ipc) {
    // deprecated
    ipc.on('message', handleMessage$1);
  }
};
const listen$1 = async (module, options) => {
  const rawIpc = await module.listen(options);
  if (module.signal) {
    module.signal(rawIpc);
  }
  const ipc = module.wrap(rawIpc);
  return ipc;
};
const create$3 = async ({
  commandMap
}) => {
  // TODO create a commandMap per rpc instance
  register$1(commandMap);
  const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  return rpc;
};
const WebWorkerRpcClient = {
  __proto__: null,
  create: create$3
};

const create$2 = () => {
  const states = Object.create(null);
  return {
    get(uid) {
      return states[uid];
    },
    set(uid, oldState, newState) {
      states[uid] = {
        oldState,
        newState
      };
    },
    dispose(uid) {
      delete states[uid];
    },
    getKeys() {
      return Object.keys(states).map(key => {
        return Number.parseInt(key);
      });
    }
  };
};

const {
  get: get$1,
  set: set$3,
  dispose: dispose$2,
  getKeys
} = create$2();

const create$1 = (uid, x, y, width, height) => {
  const state = {
    messages: [],
    uid,
    messageVersion: 0,
    selectedIndex: -1,
    x,
    y,
    width,
    height,
    headerHeight: 20,
    // TODO make this configurable
    itemHeight: 24,
    // TODO make this configurable
    messagesHeight: Math.floor(height * 0.6),
    // Add this - initially 60% height
    isResizing: false,
    resizeStartY: 0,
    resizeStartHeight: 0,
    expandedPaths: [],
    selectedContentItemHeight: 20,
    columnWidths: ['auto', '50px'],
    isFocused: false,
    filterText: '',
    filterHeight: 50,
    maxMessageLength: 100,
    minLineY: 0,
    maxLineY: 0,
    deltaY: 0,
    finalDeltaY: 0,
    focusId: 0
  };
  set$3(uid, state, state);
};

const RenderItems = 2;
const RenderHeights = 5;
const RenderFocus = 4;

const diffType$2 = RenderFocus;
const isEqual$2 = (oldState, newState) => {
  return oldState.isFocused === newState.isFocused && oldState.focusId === newState.focusId;
};

const diffType$1 = RenderHeights;
const isEqual$1 = (oldState, newState) => {
  return oldState.messagesHeight === newState.messagesHeight;
};

const diffType = RenderItems;
const isEqual = (oldState, newState) => {
  return oldState.messageVersion === newState.messageVersion && oldState.selectedIndex === newState.selectedIndex && oldState.expandedPaths === newState.expandedPaths && oldState.filterText === newState.filterText && oldState.focusId === newState.focusId;
};

const modules = [isEqual, isEqual$1, isEqual$2];
const numbers = [diffType, diffType$1, diffType$2];

const diff = (oldState, newState) => {
  const diffResult = [];
  for (let i = 0; i < modules.length; i++) {
    const fn = modules[i];
    if (!fn(oldState, newState)) {
      diffResult.push(numbers[i]);
    }
  }
  return diffResult;
};

const diff2 = uid => {
  const {
    oldState,
    newState
  } = get$1(uid);
  const diffResult = diff(oldState, newState);
  return diffResult;
};

const dispose$1 = uid => {
  dispose$2(uid);
};

const commandIds = ['handleArrowDown', 'handleArrowUp', 'handleBlur', 'handleClick', 'handleEnd', 'handleFilterInput', 'handleFocus', 'handleHome', 'handleResizerMouseDown', 'handleResizerMouseMove', 'handleResizerMouseUp', 'handleSelectedContentClick', 'selectIndex', 'terminate'];

const getCommandIds = () => {
  return commandIds;
};

const End = 255;
const Home = 12;
const UpArrow = 14;
const DownArrow = 16;

const FocusIframeInspector = 102;

const getKeyBindings$1 = () => {
  return [{
    key: DownArrow,
    command: 'IframeInspector.handleArrowDown',
    when: FocusIframeInspector
  }, {
    key: UpArrow,
    command: 'IframeInspector.handleArrowUp',
    when: FocusIframeInspector
  }, {
    key: Home,
    command: 'IframeInspector.handleHome',
    when: FocusIframeInspector
  }, {
    key: End,
    command: 'IframeInspector.handleEnd',
    when: FocusIframeInspector
  }];
};

const state = {
  messages: []
};
const addMessage = message => {
  state.messages = [...state.messages, message];
};
const getMessages = () => {
  return state.messages;
};

const Input$1 = 1;
const List = 2;

const selectIndex = (newState, index) => {
  const updatedState = {
    ...newState,
    selectedIndex: index,
    isFocused: true,
    focusId: List
  };
  return updatedState;
};

const handleArrowDown = newState => {
  const messages = getMessages();
  const nextIndex = Math.min(messages.length - 1, newState.selectedIndex + 1);
  return selectIndex(newState, nextIndex);
};

const handleArrowUp = newState => {
  const nextIndex = Math.max(0, newState.selectedIndex - 1);
  return selectIndex(newState, nextIndex);
};

const handleBlur = newState => {
  return newState;
};

const getIndex = (headerHeight, y, eventY, itemHeight, filterHeight) => {
  const top = headerHeight + filterHeight + y;
  const actualY = eventY - top;
  const index = Math.floor(actualY / itemHeight);
  return index;
};

const handleClick = (newState, eventX, eventY) => {
  const {
    headerHeight,
    y,
    itemHeight,
    filterHeight
  } = newState;
  const index = getIndex(headerHeight, y, eventY, itemHeight, filterHeight);
  return selectIndex(newState, index);
};

const handleEnd = state => {
  const messages = getMessages();
  return selectIndex(state, Math.max(0, messages.length - 1));
};

const handleFilterInput = (uid, filterText) => {
  const {
    newState
  } = get$1(uid);
  const updatedState = {
    ...newState,
    filterText,
    inputSource: 1
  };
  set$3(uid, newState, updatedState);
};

const DebugWorker = 55;
const RendererWorker$1 = 1;

const rpcs = Object.create(null);
const set$2 = (id, rpc) => {
  rpcs[id] = rpc;
};
const get = id => {
  return rpcs[id];
};

const create = rpcId => {
  return {
    // @ts-ignore
    invoke(method, ...params) {
      const rpc = get(rpcId);
      // @ts-ignore
      return rpc.invoke(method, ...params);
    },
    // @ts-ignore
    invokeAndTransfer(method, ...params) {
      const rpc = get(rpcId);
      // @ts-ignore
      return rpc.invokeAndTransfer(method, ...params);
    },
    set(rpc) {
      set$2(rpcId, rpc);
    },
    async dispose() {
      const rpc = get(rpcId);
      await rpc.dispose();
    }
  };
};

const {
  invoke,
  invokeAndTransfer: invokeAndTransfer$1,
  set: set$1,
  dispose
} = create(RendererWorker$1);
const searchFileHtml = async uri => {
  return invoke('ExtensionHost.searchFileWithHtml', uri);
};
const getFilePathElectron = async file => {
  return invoke('FileSystemHandle.getFilePathElectron', file);
};
const showContextMenu = async (x, y, id, ...args) => {
  return invoke('ContextMenu.show', x, y, id, ...args);
};
const getElectronVersion = async () => {
  return invoke('Process.getElectronVersion');
};
const applyBulkReplacement = async bulkEdits => {
  await invoke('BulkReplacement.applyBulkReplacement', bulkEdits);
};
const setColorTheme = async id => {
  // @ts-ignore
  return invoke(/* ColorTheme.setColorTheme */'ColorTheme.setColorTheme', /* colorThemeId */id);
};
const getNodeVersion = async () => {
  return invoke('Process.getNodeVersion');
};
const getChromeVersion = async () => {
  return invoke('Process.getChromeVersion');
};
const getV8Version = async () => {
  return invoke('Process.getV8Version');
};
const getFileHandles = async fileIds => {
  const files = await invoke('FileSystemHandle.getFileHandles', fileIds);
  return files;
};
const setWorkspacePath = async path => {
  await invoke('Workspace.setPath', path);
};
const registerWebViewInterceptor = async (id, port) => {
  await invokeAndTransfer$1('WebView.registerInterceptor', id, port);
};
const unregisterWebViewInterceptor = async id => {
  await invoke('WebView.unregisterInterceptor', id);
};
const sendMessagePortToEditorWorker = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  // @ts-ignore
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToEditorWorker', port, command, rpcId);
};
const sendMessagePortToErrorWorker = async (port, rpcId) => {
  const command = 'Errors.handleMessagePort';
  // @ts-ignore
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToErrorWorker', port, command, rpcId);
};
const sendMessagePortToMarkdownWorker = async (port, rpcId) => {
  const command = 'Markdown.handleMessagePort';
  // @ts-ignore
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToMarkdownWorker', port, command, rpcId);
};
const sendMessagePortToFileSystemWorker = async (port, rpcId) => {
  const command = 'FileSystem.handleMessagePort';
  // @ts-ignore
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToFileSystemWorker', port, command, rpcId);
};
const readFile = async uri => {
  return invoke('FileSystem.readFile', uri);
};
const getWebViewSecret = async key => {
  // @ts-ignore
  return invoke('WebView.getSecret', key);
};
const setWebViewPort = async (uid, port, origin, portType) => {
  return invokeAndTransfer$1('WebView.setPort', uid, port, origin, portType);
};
const setFocus$1 = key => {
  return invoke('Focus.setFocus', key);
};
const getFileIcon = async options => {
  return invoke('IconTheme.getFileIcon', options);
};
const getColorThemeNames = async () => {
  return invoke('ColorTheme.getColorThemeNames');
};
const disableExtension = async id => {
  // @ts-ignore
  return invoke('ExtensionManagement.disable', id);
};
const enableExtension = async id => {
  // @ts-ignore
  return invoke('ExtensionManagement.enable', id);
};
const handleDebugChange = async params => {
  // @ts-ignore
  return invoke('Run And Debug.handleChange', params);
};
const getFolderIcon = async options => {
  return invoke('IconTheme.getFolderIcon', options);
};
const closeWidget = async widgetId => {
  return invoke('Viewlet.closeWidget', widgetId);
};
const sendMessagePortToExtensionHostWorker = async (port, rpcId = 0) => {
  const command = 'HandleMessagePort.handleMessagePort2';
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToExtensionHostWorker', port, command, rpcId);
};
const sendMessagePortToSearchProcess = async port => {
  await invokeAndTransfer$1('SendMessagePortToElectron.sendMessagePortToElectron', port, 'HandleMessagePortForSearchProcess.handleMessagePortForSearchProcess');
};
const confirm = async (message, options) => {
  // @ts-ignore
  const result = await invoke('ConfirmPrompt.prompt', message, options);
  return result;
};
const getRecentlyOpened = async () => {
  return invoke(/* RecentlyOpened.getRecentlyOpened */'RecentlyOpened.getRecentlyOpened');
};
const getKeyBindings = async () => {
  return invoke('KeyBindingsInitial.getKeyBindings');
};
const writeClipBoardText = async text => {
  await invoke('ClipBoard.writeText', /* text */text);
};
const writeClipBoardImage = async blob => {
  // @ts-ignore
  await invoke('ClipBoard.writeImage', /* text */blob);
};
const searchFileMemory = async uri => {
  // @ts-ignore
  return invoke('ExtensionHost.searchFileWithMemory', uri);
};
const searchFileFetch = async uri => {
  return invoke('ExtensionHost.searchFileWithFetch', uri);
};
const showMessageBox = async options => {
  return invoke('ElectronDialog.showMessageBox', options);
};
const handleDebugResumed = async params => {
  await invoke('Run And Debug.handleResumed', params);
};
const openWidget = async name => {
  await invoke('Viewlet.openWidget', name);
};
const getIcons = async requests => {
  const icons = await invoke('IconTheme.getIcons', requests);
  return icons;
};
const activateByEvent = event => {
  return invoke('ExtensionHostManagement.activateByEvent', event);
};
const setAdditionalFocus = focusKey => {
  // @ts-ignore
  return invoke('Focus.setAdditionalFocus', focusKey);
};
const getActiveEditorId = () => {
  // @ts-ignore
  return invoke('GetActiveEditor.getActiveEditorId');
};
const getWorkspacePath = () => {
  return invoke('Workspace.getPath');
};
const sendMessagePortToRendererProcess = async port => {
  const command = 'HandleMessagePort.handleMessagePort';
  // @ts-ignore
  await invokeAndTransfer$1('SendMessagePortToExtensionHostWorker.sendMessagePortToRendererProcess', port, command, DebugWorker);
};
const getPreference = async key => {
  return await invoke('Preferences.get', key);
};
const getAllExtensions = async () => {
  return invoke('ExtensionManagement.getAllExtensions');
};
const rerenderEditor = async key => {
  // @ts-ignore
  return invoke('Editor.rerender', key);
};
const handleDebugPaused = async params => {
  await invoke('Run And Debug.handlePaused', params);
};
const openUri = async (uri, focus, options) => {
  await invoke('Main.openUri', uri, focus, options);
};
const sendMessagePortToSyntaxHighlightingWorker = async port => {
  await invokeAndTransfer$1(
  // @ts-ignore
  'SendMessagePortToSyntaxHighlightingWorker.sendMessagePortToSyntaxHighlightingWorker', port, 'HandleMessagePort.handleMessagePort2');
};
const handleDebugScriptParsed = async script => {
  await invoke('Run And Debug.handleScriptParsed', script);
};
const getWindowId = async () => {
  return invoke('GetWindowId.getWindowId');
};
const getBlob = async uri => {
  // @ts-ignore
  return invoke('FileSystem.getBlob', uri);
};
const getExtensionCommands = async () => {
  return invoke('ExtensionHost.getCommands');
};
const showErrorDialog = async errorInfo => {
  // @ts-ignore
  await invoke('ErrorHandling.showErrorDialog', errorInfo);
};
const getFolderSize = async uri => {
  // @ts-ignore
  return await invoke('FileSystem.getFolderSize', uri);
};
const getExtension = async id => {
  // @ts-ignore
  return invoke('ExtensionManagement.getExtension', id);
};
const getMarkdownDom = async html => {
  // @ts-ignore
  return invoke('Markdown.getVirtualDom', html);
};
const renderMarkdown = async (markdown, options) => {
  // @ts-ignore
  return invoke('Markdown.renderMarkdown', markdown, options);
};
const openNativeFolder = async uri => {
  // @ts-ignore
  await invoke('OpenNativeFolder.openNativeFolder', uri);
};
const uninstallExtension = async id => {
  return invoke('ExtensionManagement.uninstall', id);
};
const installExtension = async id => {
  // @ts-ignore
  return invoke('ExtensionManagement.install', id);
};
const openExtensionSearch = async () => {
  // @ts-ignore
  return invoke('SideBar.openViewlet', 'Extensions');
};
const setExtensionsSearchValue = async searchValue => {
  // @ts-ignore
  return invoke('Extensions.handleInput', searchValue);
};
const openExternal = async uri => {
  // @ts-ignore
  await invoke('Open.openExternal', uri);
};
const openUrl = async uri => {
  // @ts-ignore
  await invoke('Open.openUrl', uri);
};
const getAllPreferences = async () => {
  // @ts-ignore
  return invoke('Preferences.getAll');
};
const showSaveFilePicker = async () => {
  // @ts-ignore
  return invoke('FilePicker.showSaveFilePicker');
};
const getLogsDir = async () => {
  // @ts-ignore
  return invoke('PlatformPaths.getLogsDir');
};

const RendererWorker = {
  __proto__: null,
  activateByEvent,
  applyBulkReplacement,
  closeWidget,
  confirm,
  disableExtension,
  dispose,
  enableExtension,
  getActiveEditorId,
  getAllExtensions,
  getAllPreferences,
  getBlob,
  getChromeVersion,
  getColorThemeNames,
  getElectronVersion,
  getExtension,
  getExtensionCommands,
  getFileHandles,
  getFileIcon,
  getFilePathElectron,
  getFolderIcon,
  getFolderSize,
  getIcons,
  getKeyBindings,
  getLogsDir,
  getMarkdownDom,
  getNodeVersion,
  getPreference,
  getRecentlyOpened,
  getV8Version,
  getWebViewSecret,
  getWindowId,
  getWorkspacePath,
  handleDebugChange,
  handleDebugPaused,
  handleDebugResumed,
  handleDebugScriptParsed,
  installExtension,
  invoke,
  invokeAndTransfer: invokeAndTransfer$1,
  openExtensionSearch,
  openExternal,
  openNativeFolder,
  openUri,
  openUrl,
  openWidget,
  readFile,
  registerWebViewInterceptor,
  renderMarkdown,
  rerenderEditor,
  searchFileFetch,
  searchFileHtml,
  searchFileMemory,
  sendMessagePortToEditorWorker,
  sendMessagePortToErrorWorker,
  sendMessagePortToExtensionHostWorker,
  sendMessagePortToFileSystemWorker,
  sendMessagePortToMarkdownWorker,
  sendMessagePortToRendererProcess,
  sendMessagePortToSearchProcess,
  sendMessagePortToSyntaxHighlightingWorker,
  set: set$1,
  setAdditionalFocus,
  setColorTheme,
  setExtensionsSearchValue,
  setFocus: setFocus$1,
  setWebViewPort,
  setWorkspacePath,
  showContextMenu,
  showErrorDialog,
  showMessageBox,
  showSaveFilePicker,
  uninstallExtension,
  unregisterWebViewInterceptor,
  writeClipBoardImage,
  writeClipBoardText
};

const {
  setFocus
} = RendererWorker;

const handleFocus = async (newState, inputName) => {
  // Don't set focus if it's coming from the input
  await setFocus(FocusIframeInspector);
  if (newState.isFocused && newState.focusId === Input$1) {
    return newState;
  }
  const updatedState = {
    ...newState,
    isFocused: true,
    focusId: Input$1
  };
  return updatedState;
};

const handleHome = state => {
  return selectIndex(state, 0);
};

const handleListBlur = newState => {
  return newState;
};

const handleListFocus = newState => {
  return newState;
};

const handleResizerMouseDown = (uid, clientY) => {
  const {
    newState
  } = get$1(uid);
  const updatedState = {
    ...newState,
    isResizing: true,
    resizeStartY: clientY,
    resizeStartHeight: newState.messagesHeight
  };
  set$3(uid, newState, updatedState);
};
const handleResizerMouseMove = (uid, clientY) => {
  const {
    newState
  } = get$1(uid);
  if (!newState.isResizing) {
    return;
  }
  const delta = clientY - newState.resizeStartY;
  const newHeight = Math.max(50, Math.min(newState.height - 50, newState.resizeStartHeight + delta));
  const updatedState = {
    ...newState,
    messagesHeight: newHeight
  };
  set$3(uid, newState, updatedState);
};
const handleResizerMouseUp = uid => {
  const {
    newState
  } = get$1(uid);
  const updatedState = {
    ...newState,
    isResizing: false,
    resizeStartY: 0,
    resizeStartHeight: 0
  };
  set$3(uid, newState, updatedState);
};

const isExpandable = value => {
  return value !== null && (typeof value === 'object' || Array.isArray(value));
};

const createPairs = (value, parentPath, depth, expandedPaths) => {
  const pairs = [];
  const entries = Object.entries(value);
  for (const [key, childValue] of entries) {
    const path = parentPath ? `${parentPath}.${key}` : key;
    const expandable = isExpandable(childValue);
    const expanded = expandedPaths.includes(path);
    pairs.push({
      key,
      value: childValue,
      stringifiedValue: expandable ? Array.isArray(childValue) ? '[]' : '{}' : JSON.stringify(childValue),
      path,
      isExpandable: expandable,
      isExpanded: expanded,
      depth
    });
    if (expandable && expanded) {
      pairs.push(...createPairs(childValue, path, depth + 1, expandedPaths));
    }
  }
  return pairs;
};
const createSelectedMessageViewModel = (messages, selectedIndex, expandedPaths) => {
  if (selectedIndex === -1) {
    return {
      pairs: []
    };
  }
  const message = messages[selectedIndex];
  const pairs = createPairs(message, '', 0, expandedPaths);
  return {
    pairs
  };
};

const selectContentIndex = (uid, path) => {
  const {
    newState
  } = get$1(uid);
  const newExpandedPaths = newState.expandedPaths.includes(path) ? newState.expandedPaths.filter(expandedPath => expandedPath !== path) : [...newState.expandedPaths, path];
  const updatedState = {
    ...newState,
    expandedPaths: newExpandedPaths
  };
  set$3(uid, newState, updatedState);
};

const bottomHeight = 220;
const handleSelectedContentClick = (uid, eventX, eventY) => {
  const {
    newState
  } = get$1(uid);
  const topHeight = newState.height - bottomHeight;
  const relativeY = eventY - newState.y - topHeight;
  const index = Math.floor(relativeY / newState.selectedContentItemHeight);
  if (newState.selectedIndex === -1) {
    return;
  }
  const messages = getMessages();
  const selectedModel = createSelectedMessageViewModel(messages, newState.selectedIndex, newState.expandedPaths);
  if (index >= 0 && index < selectedModel.pairs.length) {
    const {
      path
    } = selectedModel.pairs[index];
    selectContentIndex(uid, path);
  }
};

const handleWheel = (uid, deltaY) => {
  const {
    newState
  } = get$1(uid);
  const updatedState = {
    ...newState,
    y: Math.max(0, newState.y + deltaY)
  };
  set$3(uid, newState, updatedState);
};

// TODO remove event listener on dispose
const handleMessage = data => {
  const actual = data.params[0];
  const message = typeof actual === 'string' ? {
    jsonrpc: '2.0',
    method: actual,
    params: []
  } : actual;
  addMessage(message);
  const keys = getKeys();
  for (const key of keys) {
    const {
      newState
    } = get$1(key);
    const updatedState = {
      ...newState,
      messageVersion: newState.messageVersion + 1
    };
    set$3(key, newState, updatedState);
  }
  // TODO if view is visible, rerender
};

const {
  invokeAndTransfer,
  set} = RendererWorker;

const register = async handleMessage => {
  const id = 1;
  const {
    port1,
    port2
  } = new MessageChannel();
  await invokeAndTransfer('WebView.registerInterceptor', id, port1);
  // TODO create rpc with port2
  // TODO remove event listener on dispose
  const wrappedHandleMessage = event => {
    handleMessage(event.data);
  };
  port2.addEventListener('message', wrappedHandleMessage);
  port2.start();
};

const registerInterceptor = async () => {
  await register(handleMessage);
};

const loadContent = async uid => {
  const {
    newState
  } = get$1(uid);
  const updated = {
    ...newState,
    messageVersion: 1
  };
  set$3(uid, newState, updated);
  await registerInterceptor();
};

const IframeInspectorFilterInput = 'IframeInspectorFilterInput';

const getSelector = focusId => {
  switch (focusId) {
    case List:
      return '.IframeInspectorGrid';
    case Input$1:
      return IframeInspectorFilterInput;
    default:
      return '';
  }
};
const renderFocus = (oldState, newState) => {
  const selector = getSelector(newState.focusId);
  return ['Viewlet.focusSelector', selector];
};

const renderHeights = (oldState, newState) => {
  return ['Viewlet.setMessagesHeight', newState.messagesHeight];
};

const getMessagePreview = (input, maxLength) => {
  if (input.length < maxLength) {
    return input;
  }
  const part = input.slice(0, maxLength);
  return part + '...';
};

const TopLevelContent = 1;
const AfterCurlyOpen = 2;
const InsideString = 3;
const AfterPropertyName = 4;
const AfterPropertyNameAfterColon = 5;
const AfterPropertyValue = 6;
const InsideLineComment = 8;
const InsidePropertyNameString = 9;
const InsideBlockComment = 10;

const Comment = 0;
const CurlyClose = 1;
const JsonPropertyName = 2;
const JsonPropertyValueString = 3;
const LanguageConstant = 4;
const Numeric = 5;
const Punctuation = 6;
const String$1 = 7;
const Text$1 = 8;
const Whitespace = 9;

const RE_WHITESPACE = /^\s+/;
const RE_CURLY_OPEN = /^\{/;
const RE_CURLY_CLOSE = /^\}/;
const RE_COLON = /^:/;
const RE_COMMA = /^,/;
const RE_DOUBLE_QUOTE = /^"/;
const RE_STRING_DOUBLE_QUOTE_CONTENT = /^[^"\\]+/;
const RE_LANGUAGE_CONSTANT = /^(?:true|false|null)/;
const RE_SQUARE_OPEN = /^\[/;
const RE_SQUARE_CLOSE = /^]/;
const RE_ANYTHING = /^.+/s;
const RE_NUMERIC = /^((0(x|X)[0-9a-fA-F]*)|(-?([0-9]+\.?[0-9]*)|(\.[0-9]+))((e|E)(\+|-)?[0-9]+)?)\b/;
const RE_TEXT = /^[^\s{}[\]]+/;
const RE_STRING_ESCAPE = /^\\.?/;
const RE_WORD = /^\w+/;
const RE_ANYTHING_UNTIL_END = /^.+/s;
const tokenizeJson = input => {
  let next;
  let index = 0;
  let token;
  let state = TopLevelContent;
  const tokens = [];
  const stack = [];
  while (index < input.length) {
    const part = input.slice(index);
    switch (state) {
      case TopLevelContent:
        if (next = part.match(RE_CURLY_OPEN)) {
          token = Punctuation;
          state = AfterCurlyOpen;
          stack.push(TopLevelContent);
        } else if (next = part.match(RE_SQUARE_OPEN)) {
          token = Punctuation;
          state = TopLevelContent;
          stack.push(TopLevelContent);
        } else if (next = part.match(RE_WHITESPACE)) {
          token = Whitespace;
          state = TopLevelContent;
        } else if (next = part.match(RE_SQUARE_CLOSE)) {
          token = Punctuation;
          state = stack.pop() || TopLevelContent;
        } else if (next = part.match(RE_COMMA)) {
          token = Punctuation;
          state = TopLevelContent;
        } else if (next = part.match(RE_NUMERIC)) {
          token = Numeric;
          state = TopLevelContent;
        } else if (next = part.match(RE_LANGUAGE_CONSTANT)) {
          token = LanguageConstant;
          state = TopLevelContent;
        } else if (next = part.match(RE_DOUBLE_QUOTE)) {
          token = Punctuation;
          state = InsideString;
          stack.push(TopLevelContent);
        } else if (next = part.match(RE_CURLY_CLOSE)) {
          token = CurlyClose;
          state = stack.pop() || TopLevelContent;
        } else if (next = part.match(RE_ANYTHING)) {
          token = Text$1;
          state = TopLevelContent;
        } else {
          throw new Error('no');
        }
        break;
      case AfterCurlyOpen:
        if (next = part.match(RE_WHITESPACE)) {
          token = Whitespace;
          state = AfterCurlyOpen;
        } else if (next = part.match(RE_DOUBLE_QUOTE)) {
          token = Punctuation;
          state = InsidePropertyNameString;
          stack.push(AfterPropertyName);
        } else if (next = part.match(RE_CURLY_CLOSE)) {
          token = Punctuation;
          state = stack.pop() || TopLevelContent;
          if (!state) {
            throw new Error('imbalanced json');
          }
        } else if (next = part.match(RE_WORD)) {
          token = Text$1;
          state = AfterPropertyName;
        } else if (next = part.match(RE_ANYTHING)) {
          token = Text$1;
          state = AfterCurlyOpen;
        } else {
          throw new Error('no');
        }
        break;
      case InsidePropertyNameString:
        if (next = part.match(RE_STRING_DOUBLE_QUOTE_CONTENT)) {
          token = JsonPropertyName;
          state = InsidePropertyNameString;
        } else if (next = part.match(RE_DOUBLE_QUOTE)) {
          token = Punctuation;
          state = stack.pop() || TopLevelContent;
        } else if (next = part.match(RE_STRING_ESCAPE)) {
          token = JsonPropertyName;
          state = InsidePropertyNameString;
        } else {
          throw new Error('no');
        }
        break;
      case InsideString:
        if (next = part.match(RE_STRING_DOUBLE_QUOTE_CONTENT)) {
          token = JsonPropertyValueString;
          state = InsideString;
        } else if (next = part.match(RE_DOUBLE_QUOTE)) {
          token = Punctuation;
          state = stack.pop() || TopLevelContent;
        } else if (next = part.match(RE_STRING_ESCAPE)) {
          token = String$1;
          state = InsideString;
        } else {
          throw new Error('no');
        }
        break;
      case AfterPropertyName:
        if (next = part.match(RE_COLON)) {
          token = Punctuation;
          state = AfterPropertyNameAfterColon;
        } else if (next = part.match(RE_WHITESPACE)) {
          token = Whitespace;
          state = AfterPropertyName;
        } else if (next = part.match(RE_ANYTHING)) {
          token = Text$1;
          state = AfterCurlyOpen;
        } else {
          throw new Error('no');
        }
        break;
      case AfterPropertyNameAfterColon:
        if (next = part.match(RE_WHITESPACE)) {
          token = Whitespace;
          state = AfterPropertyNameAfterColon;
        } else if (next = part.match(RE_DOUBLE_QUOTE)) {
          token = Punctuation;
          state = InsideString;
          stack.push(AfterPropertyValue);
        } else if (next = part.match(RE_LANGUAGE_CONSTANT)) {
          token = LanguageConstant;
          state = AfterPropertyValue;
        } else if (next = part.match(RE_NUMERIC)) {
          token = Numeric;
          state = AfterPropertyValue;
        } else if (next = part.match(RE_TEXT)) {
          token = Text$1;
          state = AfterCurlyOpen;
        } else if (next = part.match(RE_CURLY_OPEN)) {
          token = Punctuation;
          state = AfterCurlyOpen;
          stack.push(AfterPropertyValue);
        } else if (next = part.match(RE_SQUARE_OPEN)) {
          token = Punctuation;
          state = TopLevelContent;
          stack.push(AfterPropertyValue);
        } else if (next = part.match(RE_CURLY_CLOSE)) {
          token = Punctuation;
          state = stack.pop() || TopLevelContent;
        } else {
          throw new Error('no');
        }
        break;
      case AfterPropertyValue:
        if (next = part.match(RE_CURLY_CLOSE)) {
          token = Punctuation;
          state = stack.pop() || TopLevelContent;
          if (!state) {
            throw new Error('inbalanced json');
          }
        } else if (next = part.match(RE_WHITESPACE)) {
          token = Whitespace;
          state = AfterPropertyValue;
        } else if (next = part.match(RE_COMMA)) {
          token = Punctuation;
          state = AfterCurlyOpen;
        } else if (next = part.match(RE_COLON)) {
          token = Punctuation;
          state = AfterPropertyNameAfterColon;
        } else if (next = part.match(RE_ANYTHING)) {
          token = Text$1;
          state = AfterCurlyOpen;
        } else {
          throw new Error('no');
        }
        break;
      case InsideLineComment:
        if (next = part.match(RE_ANYTHING)) {
          token = Comment;
          state = TopLevelContent;
        } else {
          throw new Error('no');
        }
        break;
      case InsideBlockComment:
        if (next = part.match(RE_ANYTHING_UNTIL_END)) {
          token = Comment;
          state = InsideBlockComment;
        } else {
          throw new Error('no');
        }
        break;
      default:
        throw new Error('no');
    }
    const currentTokenText = next[0];
    const currentTokenLength = currentTokenText.length;
    index += currentTokenLength;
    tokens.push({
      tokenType: token,
      tokenText: currentTokenText
    });
  }
  return tokens;
};

const createMessageViewModel = (message, maxLength, index, selectedIndex, isListFocused) => {
  const stringified = JSON.stringify(message);
  const preview = getMessagePreview(stringified, maxLength);
  const tokens = tokenizeJson(preview);
  return {
    isSelected: isListFocused && index === selectedIndex,
    messagePreviewLength: String(preview.length),
    isEven: index % 2 === 0,
    messageTokens: tokens
  };
};

const createMessageViewModels = (messages, selectedIndex, maxLength, filterText, isListFocused) => {
  const viewModels = [];
  for (let i = 0; i < messages.length; i++) {
    const message = messages[i];
    viewModels.push(createMessageViewModel(message, maxLength, i, selectedIndex, isListFocused));
  }
  return viewModels;
};

function matchesFilterText(message, filterText) {
  if (!filterText) {
    return true;
  }
  const searchText = JSON.stringify(message).toLowerCase();
  return searchText.includes(filterText.toLowerCase());
}

const getFilteredMessages = (messages, filterText) => {
  if (!filterText) {
    return messages;
  }
  return messages.filter(message => matchesFilterText(message, filterText));
};

const IFrameInspector = 'IframeInspector';
const IframeInspectorResizer = 'IframeInspectorResizer';
const IframeInspectorSelectedContent = 'IframeInspectorSelectedContent';
const IframeInspectorSelectedContentItem = 'IframeInspectorSelectedContentItem';
const IframeInspectorSelectedContentKey = 'IframeInspectorSelectedContentKey';
const IframeInspectorSelectedContentList = 'IframeInspectorSelectedContentList';
const SearchField = 'SearchField';
const IframeInspectorGrid = 'IframeInspectorGrid';
const IframeInspectorSelectedContentValue = 'IframeInspectorSelectedContentValue';
const IframeInspectorExpandIcon = 'IframeInspectorExpandIcon';
const Table$1 = 'Table';
const TableBody = 'TableBody';
const TableCell = 'TableCell';
const TableHead = 'TableHead';
const TableRow = 'TableRow';
const IframeInspectorCode = 'IframeInspectorCode';
const TableRowEven = 'TableRowEven';
const TableRowOdd = 'TableRowOdd';
const IframeInspectorNoMessagesFound = 'IframeInspectorNoMessagesFound';
const TableRowSelected = 'TableRowSelected';
const TableWrapper = 'TableWrapper';
const Viewlet = 'Viewlet';
const ColGroup$1 = 'ColGroup';
const Col$1 = 'Col';
const FilterSection = 'FilterSection';
const FilterInput = 'FilterInput';

const HandleFilterInput = 'handleFilterInput';
const HandleClick = 'handleClick';
const HandleResizerMouseDown = 'handleResizerMouseDown';
const HandleResizerMouseMove = 'handleResizerMouseMove';
const HandleResizerMouseUp = 'handleResizerMouseUp';
const HandleSelectedContentClick = 'handleSelectedContentClick';
const HandleFocus = 'handleFocus';
const HandleListFocus = 'handleListFocus';
const HandleBlur = 'handleBlur';
const HandleListBlur = 'handleListBlur';
const HandleWheel = 'handleWheel';

const emptyObject = {};
const RE_PLACEHOLDER = /\{(PH\d+)\}/g;
const i18nString = (key, placeholders = emptyObject) => {
  if (placeholders === emptyObject) {
    return key;
  }
  const replacer = (match, rest) => {
    return placeholders[rest];
  };
  return key.replaceAll(RE_PLACEHOLDER, replacer);
};

const NoMessageSelected = 'No message selected';
const NoMessagesFound = 'No messages found';
const FilterMessages = 'Filter messages...';

const noMessagesAvailable = () => {
  return i18nString(NoMessagesFound);
};
const filterMessages = () => {
  return i18nString(FilterMessages);
};

const Col = 2;
const ColGroup = 3;
const Div = 4;
const Input = 6;
const Span = 8;
const Table = 9;
const TBody = 10;
const Td = 11;
const Text = 12;
const Th = 13;
const THead = 14;
const Tr = 15;
const Li = 48;
const Ol = 49;
const Code = 65;

const getFilterVirtualDom = () => {
  const placeholder = filterMessages();
  return [{
    type: Div,
    className: FilterSection,
    childCount: 1
  }, {
    type: Div,
    className: SearchField,
    childCount: 1
  }, {
    type: Input,
    className: FilterInput,
    placeholder,
    onInput: HandleFilterInput,
    onFocus: HandleFocus,
    name: IframeInspectorFilterInput
  }];
};

const getResizerVirtualDom = () => {
  return [{
    type: Div,
    className: IframeInspectorResizer,
    childCount: 0,
    onPointerDown: HandleResizerMouseDown,
    onPointerMove: HandleResizerMouseMove,
    onPointerUp: HandleResizerMouseUp
  }];
};

const text = data => {
  return {
    type: Text,
    text: data,
    childCount: 0
  };
};

const getKeyValuePairVirtualDom = pair => {
  const nodes = [{
    type: Li,
    className: IframeInspectorSelectedContentItem,
    paddingLeft: `${pair.depth * 20}px`,
    childCount: pair.isExpandable ? 4 : 3
  }];
  if (pair.isExpandable) {
    nodes.push({
      type: Span,
      className: IframeInspectorExpandIcon,
      childCount: 1
    }, text(pair.isExpanded ? '▼' : '▶'));
  }
  nodes.push({
    type: Span,
    className: IframeInspectorSelectedContentKey,
    childCount: 1
  }, text(pair.key), text(': '), {
    type: Span,
    className: IframeInspectorSelectedContentValue,
    childCount: 1
  }, text(pair.stringifiedValue));
  return nodes;
};

const getSelectedContentVirtualDom = (viewModel, selectedIndex) => {
  if (selectedIndex === -1) {
    return [{
      type: Div,
      className: IframeInspectorSelectedContent,
      childCount: 1
    }, text(NoMessageSelected)];
  }
  return [{
    type: Div,
    className: IframeInspectorSelectedContent,
    childCount: 1
  }, {
    type: Ol,
    className: IframeInspectorSelectedContentList,
    childCount: viewModel.pairs.length,
    onPointerDown: HandleSelectedContentClick
  }, ...viewModel.pairs.flatMap(getKeyValuePairVirtualDom)];
};

const getIframeInspectorBottomVirtualDom = (messages, selectedModel, selectedIndex) => {
  if (messages.length === 0) {
    return [];
  }
  return [...getResizerVirtualDom(), ...getSelectedContentVirtualDom(selectedModel, selectedIndex)];
};

const getNoMessagesFoundVirtualDom = () => {
  const noMessagesFound = noMessagesAvailable();
  return [{
    type: Div,
    className: IframeInspectorNoMessagesFound,
    childCount: 1
  }, text(noMessagesFound)];
};

const getColVirtualDom = width => {
  return {
    type: Col,
    className: Col$1,
    width,
    childCount: 0
  };
};
const getColGroupVirtualDom = columnWidths => {
  return [{
    type: ColGroup,
    className: ColGroup$1,
    childCount: columnWidths.length
  }, ...columnWidths.map(getColVirtualDom)];
};

const getHeaderVirtualDom = () => {
  return [{
    type: THead,
    className: TableHead,
    childCount: 1
  }, {
    type: Tr,
    className: TableRow,
    childCount: 2
  }, {
    type: Th,
    className: TableCell,
    childCount: 1
  }, text('Data'), {
    type: Th,
    className: TableCell,
    childCount: 1
  }, text('Length')];
};

const joinBySpace = (...items) => {
  return items.join(' ');
};

const mergeClassNames = (...classNames) => {
  return joinBySpace(...classNames.filter(Boolean));
};

const getMessageClassName = (isSelected, isEven) => {
  return mergeClassNames(TableRow, isSelected ? TableRowSelected : '', isEven ? TableRowEven : TableRowOdd);
};

const tableCell = {
  type: Td,
  className: TableCell,
  childCount: 1
};
const span = {
  type: Span,
  className: mergeClassNames('Token Numeric'),
  childCount: 1
};
const getMessageLengthVirtualDom = messageLength => {
  return [tableCell, span, text(messageLength)];
};

const createTokenNode = className => {
  return {
    type: Span,
    className: `Token ${className}`,
    childCount: 1
  };
};
const tokenParentNodes = [createTokenNode('Comment'), createTokenNode('CurlyClose'), createTokenNode('JsonPropertyName'), createTokenNode('JsonPropertyValueString'), createTokenNode('LanguageConstant'), createTokenNode('Numeric'), createTokenNode('Punctuation'), createTokenNode('String'), createTokenNode('Text'), createTokenNode('Whitespace')];

const getTokenVirtualDom = token => {
  const {
    tokenType,
    tokenText
  } = token;
  return [tokenParentNodes[tokenType], text(tokenText)];
};

const getTokensVirtualDom = messageTokens => {
  return messageTokens.flatMap(getTokenVirtualDom);
};

const getMessagePreviewVirtualDom = messageTokens => {
  return [{
    type: Td,
    className: TableCell,
    childCount: 1
  }, {
    type: Code,
    className: IframeInspectorCode,
    childCount: messageTokens.length
  }, ...getTokensVirtualDom(messageTokens)];
};

const getMessageVirtualDom = message => {
  const {
    isSelected,
    messagePreviewLength,
    isEven,
    messageTokens
  } = message;
  return [{
    type: Tr,
    className: getMessageClassName(isSelected, isEven),
    childCount: 2
  }, ...getMessagePreviewVirtualDom(messageTokens), ...getMessageLengthVirtualDom(messagePreviewLength)];
};

const getMessagesVirtualDom = messages => {
  return [{
    type: TBody,
    className: TableBody,
    childCount: messages.length,
    onPointerDown: HandleClick
  }, ...messages.flatMap(getMessageVirtualDom)];
};

const getTableVirtualDom = (messages, columnWidths) => {
  return [{
    type: Table,
    className: Table$1,
    childCount: 3
  }, ...getColGroupVirtualDom(columnWidths), ...getHeaderVirtualDom(), ...getMessagesVirtualDom(messages)];
};

const Application = 'application';

const parentNode = {
  type: Div,
  className: TableWrapper,
  childCount: 1
};
const getTableWrapperVirtualDom = (messages, columnWidths) => {
  if (messages.length === 0) {
    return getNoMessagesFoundVirtualDom();
  }
  return [parentNode, {
    type: Div,
    className: IframeInspectorGrid,
    role: Application,
    tabIndex: 0,
    childCount: 1,
    onFocusIn: HandleListFocus,
    onBlur: HandleListBlur
  }, ...getTableVirtualDom(messages, columnWidths)];
};

const getIframeInspectorVirtualDom = (messages, selectedModel, selectedIndex, columnWidths) => {
  const hasMessages = messages.length > 0;
  return [{
    type: Div,
    className: mergeClassNames(Viewlet, IFrameInspector),
    childCount: hasMessages ? 4 : 2
  }, ...getFilterVirtualDom(), ...getTableWrapperVirtualDom(messages, columnWidths), ...getIframeInspectorBottomVirtualDom(messages, selectedModel, selectedIndex)];
};

const renderMessage = (oldState, newState) => {
  const messages = getMessages();
  const filteredMessages = getFilteredMessages(messages, newState.filterText);
  const isListFocused = newState.focusId === List;
  const viewModels = createMessageViewModels(filteredMessages, newState.selectedIndex, newState.maxMessageLength, newState.filterText, isListFocused);
  const selectedModel = createSelectedMessageViewModel(messages, newState.selectedIndex, newState.expandedPaths);
  const dom = getIframeInspectorVirtualDom(viewModels, selectedModel, newState.selectedIndex, newState.columnWidths);
  return ['Viewlet.setDom2', dom];
};

const getRenderer = diffType => {
  switch (diffType) {
    case RenderItems:
      return renderMessage;
    case RenderHeights:
      return renderHeights;
    case RenderFocus:
      return renderFocus;
    default:
      throw new Error('unknown renderer');
  }
};

const applyRender = (oldState, newState, diffResult) => {
  const commands = [];
  for (const item of diffResult) {
    const fn = getRenderer(item);
    commands.push(fn(oldState, newState));
  }
  return commands;
};

const render2 = (uid, diffResult) => {
  const {
    oldState,
    newState
  } = get$1(uid);
  set$3(uid, newState, newState);
  const commands = applyRender(oldState, newState, diffResult);
  return commands;
};

const renderEventListeners = () => {
  return [{
    name: HandleClick,
    params: ['handleClick', 'event.clientX', 'event.clientY'],
    preventDefault: true
  }, {
    name: HandleResizerMouseDown,
    params: ['handleResizerMouseDown', 'event.clientY'],
    preventDefault: true
  }, {
    name: HandleResizerMouseMove,
    params: ['handleResizerMouseMove', 'event.clientY'],
    preventDefault: true
  }, {
    name: HandleResizerMouseUp,
    params: ['handleResizerMouseUp'],
    preventDefault: true
  }, {
    name: HandleSelectedContentClick,
    params: ['handleSelectedContentClick', 'event.clientX', 'event.clientY'],
    preventDefault: true
  }, {
    name: HandleFocus,
    params: ['handleFocus']
  }, {
    name: HandleListFocus,
    params: ['handleListFocus']
  }, {
    name: HandleBlur,
    params: ['handleBlur']
  }, {
    name: HandleListBlur,
    params: ['handleListBlur']
  }, {
    name: HandleFilterInput,
    params: ['handleFilterInput', 'event.target.value']
  }, {
    name: HandleWheel,
    params: ['handleWheel', 'event.deltaY'],
    preventDefault: true
  }];
};

const getSavedFilterText = state => {
  if (state && typeof state === 'object' && 'filterText' in state && typeof state['filterText'] === 'string') {
    return state.filterText;
  }
  return '';
};

const restoreState = state => {
  const filterText = getSavedFilterText(state);
  return {
    filterText
  };
};

const saveState = uid => {
  const {
    newState
  } = get$1(uid);
  return {
    filterText: newState.filterText
  };
};

const terminate = () => {
  globalThis.close();
};

const wrapCommand = fn => {
  const wrapped = async (uid, ...args) => {
    const {
      newState
    } = get$1(uid);
    const newerState = await fn(newState, ...args);
    if (newState === newerState) {
      return;
    }
    set$3(uid, newState, newerState);
  };
  return wrapped;
};

const commandMap = {
  'IframeInspector.create': create$1,
  'IframeInspector.diff2': diff2,
  'IframeInspector.dispose': dispose$1,
  'IframeInspector.getCommandIds': getCommandIds,
  'IframeInspector.getKeyBindings': getKeyBindings$1,
  'IframeInspector.handleArrowDown': wrapCommand(handleArrowDown),
  'IframeInspector.handleArrowUp': wrapCommand(handleArrowUp),
  'IframeInspector.handleBlur': wrapCommand(handleBlur),
  'IframeInspector.handleClick': wrapCommand(handleClick),
  'IframeInspector.handleEnd': wrapCommand(handleEnd),
  'IframeInspector.handleFilterInput': wrapCommand(handleFilterInput),
  'IframeInspector.handleFocus': wrapCommand(handleFocus),
  'IframeInspector.handleHome': wrapCommand(handleHome),
  'IframeInspector.handleListBlur': wrapCommand(handleListBlur),
  'IframeInspector.handleListFocus': wrapCommand(handleListFocus),
  'IframeInspector.handleResizerMouseDown': wrapCommand(handleResizerMouseDown),
  'IframeInspector.handleResizerMouseMove': wrapCommand(handleResizerMouseMove),
  'IframeInspector.handleResizerMouseUp': wrapCommand(handleResizerMouseUp),
  'IframeInspector.handleSelectedContentClick': wrapCommand(handleSelectedContentClick),
  'IframeInspector.handleWheel': wrapCommand(handleWheel),
  'IframeInspector.loadContent': loadContent,
  'IframeInspector.render2': render2,
  'IframeInspector.renderEventListeners': renderEventListeners,
  'IframeInspector.restoreState': restoreState,
  'IframeInspector.saveState': saveState,
  'IframeInspector.selectIndex': wrapCommand(selectIndex),
  'IframeInspector.terminate': terminate
};

const listen = async () => {
  const rpc = await WebWorkerRpcClient.create({
    commandMap: commandMap
  });
  set(rpc);
};

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

main();
