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;
    }
  }
}

class AssertionError extends Error {
  constructor(message) {
    super(message);
    this.name = 'AssertionError';
  }
}
const Object$1 = 1;
const Number$1 = 2;
const Array$1 = 3;
const String = 4;
const Boolean = 5;
const Function = 6;
const Null = 7;
const Unknown = 8;
const getType = value => {
  switch (typeof value) {
    case 'number':
      return Number$1;
    case 'function':
      return Function;
    case 'string':
      return String;
    case 'object':
      if (value === null) {
        return Null;
      }
      if (Array.isArray(value)) {
        return Array$1;
      }
      return Object$1;
    case 'boolean':
      return Boolean;
    default:
      return Unknown;
  }
};
const number = value => {
  const type = getType(value);
  if (type !== Number$1) {
    throw new AssertionError('expected value to be of type number');
  }
};

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 {
    code: ERR_MODULE_NOT_FOUND,
    message
  };
};
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 {
    code: E_INCOMPATIBLE_NATIVE_MODULE,
    message: `Incompatible native node module: ${message}`
  };
};
const getModuleSyntaxError = () => {
  return {
    code: E_MODULES_NOT_SUPPORTED_IN_ELECTRON,
    message: `ES Modules are 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 {
    code: '',
    message: actualMessage,
    stack: rest
  };
};
class IpcError extends VError {
  // @ts-ignore
  constructor(betterMessage, stdout = '', stderr = '') {
    if (stdout || stderr) {
      // @ts-ignore
      const {
        code,
        message,
        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 {
    promise,
    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({
      id: firstMessage.id,
      jsonrpc: '2.0',
      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$1 = '2.0';
const callbacks = Object.create(null);
const get$2 = id => {
  return callbacks[id];
};
const remove$1 = id => {
  delete callbacks[id];
};
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$1(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$1,
    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$3 = (message, result) => {
  return {
    jsonrpc: Two$1,
    id: message.id,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$3(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
  return {
    jsonrpc: Two$1,
    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');
};

class CommandNotFoundError extends Error {
  constructor(command) {
    super(`Command not found ${command}`);
    this.name = 'CommandNotFoundError';
  }
}
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 CommandNotFoundError(command);
  }
  return fn(...args);
};

const Two = '2.0';
const create$t = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};
const create$s = (id, method, params) => {
  const message = {
    id,
    jsonrpc: Two,
    method,
    params
  };
  return message;
};
let id$1 = 0;
const create$r = () => {
  return ++id$1;
};

/* eslint-disable n/no-unsupported-features/es-syntax */

const registerPromise = map => {
  const id = create$r();
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  map[id] = resolve;
  return {
    id,
    promise
  };
};

// @ts-ignore
const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
  const {
    id,
    promise
  } = registerPromise(callbacks);
  const message = create$s(id, method, params);
  if (useSendAndTransfer && ipc.sendAndTransfer) {
    ipc.sendAndTransfer(message);
  } else {
    ipc.send(message);
  }
  const responseMessage = await promise;
  return unwrapJsonRpcResult(responseMessage);
};
const createRpc = ipc => {
  const callbacks = Object.create(null);
  ipc._resolve = (id, response) => {
    const fn = callbacks[id];
    if (!fn) {
      console.warn(`callback ${id} may already be disposed`);
      return;
    }
    fn(response);
    delete callbacks[id];
  };
  const rpc = {
    async dispose() {
      await ipc?.dispose();
    },
    invoke(method, ...params) {
      return invokeHelper(callbacks, ipc, method, params, false);
    },
    invokeAndTransfer(method, ...params) {
      return invokeHelper(callbacks, ipc, method, params, true);
    },
    // @ts-ignore
    ipc,
    /**
     * @deprecated
     */
    send(method, ...params) {
      const message = create$t(method, params);
      ipc.send(message);
    }
  };
  return rpc;
};
const requiresSocket = () => {
  return false;
};
const preparePrettyError = error => {
  return error;
};
const logError = () => {
  // handled by renderer worker
};
const handleMessage = event => {
  const actualRequiresSocket = event?.target?.requiresSocket || requiresSocket;
  const actualExecute = event?.target?.execute || execute;
  return handleJsonRpcMessage(event.target, event.data, actualExecute, event.target._resolve, preparePrettyError, logError, actualRequiresSocket);
};
const handleIpc = ipc => {
  if ('addEventListener' in ipc) {
    ipc.addEventListener('message', handleMessage);
  } else if ('on' in ipc) {
    // deprecated
    ipc.on('message', handleMessage);
  }
};
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$2$1 = async ({
  commandMap
}) => {
  // TODO create a commandMap per rpc instance
  register(commandMap);
  const ipc = await listen$1(IpcChildWithModuleWorkerAndMessagePort$1);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  return rpc;
};
const WebWorkerRpcClient = {
  __proto__: null,
  create: create$2$1
};
const createMockRpc = ({
  commandMap
}) => {
  const invocations = [];
  const invoke = (method, ...params) => {
    invocations.push([method, ...params]);
    const command = commandMap[method];
    if (!command) {
      throw new Error(`command ${method} not found`);
    }
    return command(...params);
  };
  const mockRpc = {
    invocations,
    invoke,
    invokeAndTransfer: invoke
  };
  return mockRpc;
};

const Button$2 = 1;
const Div = 4;
const Input = 6;
const Table$1 = 9;
const TBody = 10;
const Td = 11;
const Text = 12;
const Th = 13;
const THead = 14;
const Tr = 15;
const P = 50;
const Reference = 100;

const Button$1 = 'event.button';
const ClientX = 'event.clientX';
const ClientY = 'event.clientY';
const TargetName = 'event.target.name';
const TargetValue = 'event.target.value';

const Script = 2;

const RendererWorker = 1;

const FocusElementByName = 'Viewlet.focusElementByName';
const SetCss = 'Viewlet.setCss';
const SetDom2 = 'Viewlet.setDom2';
const SetValueByName = 'Viewlet.setValueByName';
const SetPatches = 'Viewlet.setPatches';

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

/* eslint-disable  @typescript-eslint/explicit-function-return-type */
const create$2 = rpcId => {
  return {
    async dispose() {
      const rpc = get$1(rpcId);
      await rpc.dispose();
    },
    // @ts-ignore
    invoke(method, ...params) {
      const rpc = get$1(rpcId);
      // @ts-ignore
      return rpc.invoke(method, ...params);
    },
    // @ts-ignore
    invokeAndTransfer(method, ...params) {
      const rpc = get$1(rpcId);
      // @ts-ignore
      return rpc.invokeAndTransfer(method, ...params);
    },
    registerMockRpc(commandMap) {
      const mockRpc = createMockRpc({
        commandMap
      });
      set$2(rpcId, mockRpc);
      // @ts-ignore
      mockRpc[Symbol.dispose] = () => {
        remove(rpcId);
      };
      // @ts-ignore
      return mockRpc;
    },
    set(rpc) {
      set$2(rpcId, rpc);
    }
  };
};

const {
  invoke,
  set: set$1
} = create$2(RendererWorker);
const showContextMenu2 = async (uid, menuId, x, y, args) => {
  number(uid);
  number(menuId);
  number(x);
  number(y);
  await invoke('ContextMenu.show2', uid, menuId, x, y, args);
};

const toCommandId = key => {
  const dotIndex = key.indexOf('.');
  return key.slice(dotIndex + 1);
};
const create$1 = () => {
  const states = Object.create(null);
  const commandMapRef = {};
  return {
    clear() {
      for (const key of Object.keys(states)) {
        delete states[key];
      }
    },
    diff(uid, modules, numbers) {
      const {
        newState,
        oldState
      } = states[uid];
      const diffResult = [];
      for (let i = 0; i < modules.length; i++) {
        const fn = modules[i];
        if (!fn(oldState, newState)) {
          diffResult.push(numbers[i]);
        }
      }
      return diffResult;
    },
    dispose(uid) {
      delete states[uid];
    },
    get(uid) {
      return states[uid];
    },
    getCommandIds() {
      const keys = Object.keys(commandMapRef);
      const ids = keys.map(toCommandId);
      return ids;
    },
    getKeys() {
      return Object.keys(states).map(key => {
        return Number.parseInt(key);
      });
    },
    registerCommands(commandMap) {
      Object.assign(commandMapRef, commandMap);
    },
    set(uid, oldState, newState) {
      states[uid] = {
        newState,
        oldState
      };
    },
    wrapCommand(fn) {
      const wrapped = async (uid, ...args) => {
        const {
          newState,
          oldState
        } = states[uid];
        const newerState = await fn(newState, ...args);
        if (oldState === newerState || newState === newerState) {
          return;
        }
        const latestOld = states[uid];
        const latestNew = {
          ...latestOld.newState,
          ...newerState
        };
        states[uid] = {
          newState: latestNew,
          oldState: latestOld.oldState
        };
      };
      return wrapped;
    },
    wrapGetter(fn) {
      const wrapped = (uid, ...args) => {
        const {
          newState
        } = states[uid];
        return fn(newState, ...args);
      };
      return wrapped;
    }
  };
};
const terminate = () => {
  globalThis.close();
};

const {
  diff,
  get,
  getCommandIds,
  registerCommands,
  set,
  wrapCommand,
  wrapGetter
} = create$1();

// TODO parentUid might ot be needed
const create = (id, uri, x, y, width, height, args, parentUid, platform = 0) => {
  const state = {
    cacheKey: 'disabled-models',
    cacheName: 'language-models-cache',
    filteredModels: [],
    filterValue: '',
    focused: 'none',
    headerHeight: 50,
    initial: true,
    inputSource: 1,
    models: [],
    platform,
    rowHeight: 20,
    scrollBarHeight: 0,
    uid: id,
    width,
    x,
    y
  };
  set(id, state, state);
};

const diffCss = (oldState, newState) => {
  return oldState.headerHeight === newState.headerHeight && oldState.rowHeight === newState.rowHeight;
};

const diffFilterValue = (oldState, newState) => {
  // Only check filterValue if input source is script (2)
  // If input source is user (1), we don't need to rerender the input
  if (newState.inputSource === 1) {
    return true;
  }
  return oldState.filterValue === newState.filterValue;
};

const diffFocus = (oldState, newState) => {
  return oldState.focused === newState.focused;
};

const isEqual = (oldState, newState) => {
  return oldState.models === newState.models && oldState.filteredModels === newState.filteredModels;
};

const RenderItems = 4;
const RenderInputValue = 5;
const RenderFocus = 6;
const RenderCss = 11;
const RenderIncremental = 12;
const RenderFilterValue = 13;

const modules = [isEqual, diffFilterValue, diffFocus, diffCss];
const numbers = [RenderIncremental, RenderFilterValue, RenderFocus, RenderCss];

const diff2 = uid => {
  return diff(uid, modules, numbers);
};

const getDisabledModels = async (cacheName, cacheKey) => {
  try {
    const cache = await caches.open(cacheName);
    const response = await cache.match(cacheKey);
    if (!response) {
      return [];
    }
    const data = await response.json();
    if (Array.isArray(data.disabledModelIds)) {
      return data.disabledModelIds.filter(id => typeof id === 'string');
    }
    return [];
  } catch (error) {
    console.error('Error reading from cache storage:', error);
    return [];
  }
};
const saveDisabledModels = async (disabledModelIds, cacheName, cacheKey) => {
  try {
    const cache = await caches.open(cacheName);
    const response = Response.json({
      disabledModelIds
    }, {
      headers: {
        'Content-Type': 'application/json'
      }
    });
    await cache.put(cacheKey, response);
  } catch (error) {
    console.error('Error writing to cache storage:', error);
  }
};

const isEnabled = model => model.enabled;
const getModelId = model => model.id;
const getDisabledModelIds = models => models.filter(model => !isEnabled(model)).map(getModelId);

const updateModel = async (state, modelId, enabled) => {
  const {
    cacheKey,
    cacheName,
    filteredModels,
    models
  } = state;
  const updatedModels = models.map(model => {
    if (model.id === modelId) {
      return {
        ...model,
        enabled
      };
    }
    return model;
  });
  const updatedFilteredModels = filteredModels.map(model => {
    if (model.id === modelId) {
      return {
        ...model,
        enabled
      };
    }
    return model;
  });

  // Get all disabled models
  const disabledModels = getDisabledModelIds(updatedModels);

  // Save to cache
  await saveDisabledModels(disabledModels, cacheName, cacheKey);
  return {
    ...state,
    filteredModels: updatedFilteredModels,
    models: updatedModels
  };
};

const disableModel$1 = (state, modelId) => {
  return updateModel(state, modelId, false);
};

const enableModel$1 = (state, modelId) => {
  return updateModel(state, modelId, true);
};

const getKeyBindings = () => {
  return [];
};

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 FilterLanguageModels = 'Filter language models...';
const AddModels = 'Add Models';
const Clear = 'Clear';
const Id = 'ID';
const Name = 'Name';
const Provider = 'Provider';
const ContextSize = 'Context Size';
const NoMatchingModels$1 = 'No matching models have been found';
const EnableModel = 'Enable Model';
const DisableModel = 'Disable Model';

const addModels = () => {
  return i18nString(AddModels);
};
const clear = () => {
  return i18nString(Clear);
};
const filterLanguageModels = () => {
  return i18nString(FilterLanguageModels);
};
const id = () => {
  return i18nString(Id);
};
const name = () => {
  return i18nString(Name);
};
const provider = () => {
  return i18nString(Provider);
};
const contextSize = () => {
  return i18nString(ContextSize);
};
const noMatchingModels = () => {
  return i18nString(NoMatchingModels$1);
};
const enableModel = () => {
  return i18nString(EnableModel);
};
const disableModel = () => {
  return i18nString(DisableModel);
};

const getMenuEntries = (state, options) => {
  const {
    filteredModels
  } = state;
  const {
    modelId
  } = options;

  // Find the model with the provided modelId
  const model = filteredModels.find(m => m.id === modelId);
  if (!model) {
    return [];
  }
  const entries = [];

  // Add Disable Model entry if the model is currently enabled
  if (model.enabled) {
    entries.push({
      args: [model.id],
      command: 'LanguageModels.disableModel',
      flags: 0,
      id: 'disable-model',
      label: disableModel()
    });
  } else {
    entries.push({
      args: [model.id],
      command: 'LanguageModels.enableModel',
      flags: 0,
      id: 'enable-model',
      label: enableModel()
    });
  }
  return entries;
};

const getMenuEntryIds = () => {
  return [96];
};

const handleAddModelsClick = state => {
  // Create a dummy model
  const dummyModel = {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: `dummy-model-${Date.now()}`,
    inputContextSize: 4096,
    name: 'Dummy Model',
    outputContextSize: 2048,
    provider: 'mock-provider',
    selected: false
  };

  // Add the dummy model to the models array
  const newModels = [...state.models, dummyModel];

  // Also update filteredModels if there's no active filter
  const newFilteredModels = state.filterValue === '' ? newModels : state.filteredModels;
  return {
    ...state,
    filteredModels: newFilteredModels,
    models: newModels
  };
};

const handleCheckboxChange = async (state, targetName, targetChecked) => {
  const {
    models
  } = state;
  const targetCheckedBoolean = targetChecked === 'on' ? true : false;

  // Find the model by matching checkbox name to model id
  const rowIndex = models.findIndex(model => model.id === targetName);

  // Invalid row index
  if (rowIndex === -1 || rowIndex >= models.length) {
    return state;
  }
  const model = models[rowIndex];
  if (!model) {
    return state;
  }
  const newEnabled = !targetCheckedBoolean;

  // Update the model with the new enabled state
  return updateModel(state, model.id, newEnabled);
};

const handleClearClick = state => {
  return {
    ...state,
    filteredModels: state.models,
    filterValue: '',
    focused: 'input',
    inputSource: Script
  };
};

const matchesFilterValue = (model, lowerValue) => {
  const {
    id,
    name,
    provider
  } = model;
  return name.toLowerCase().includes(lowerValue) || id.toLowerCase().includes(lowerValue) || provider.toLowerCase().includes(lowerValue);
};

const getFilteredModels = (models, value) => {
  if (value === '') {
    return [...models];
  }
  const lowerValue = value.toLowerCase();
  return models.filter(model => matchesFilterValue(model, lowerValue));
};

const handleFilterInput = (state, value) => {
  const {
    models
  } = state;
  const filteredModels = getFilteredModels(models, value);
  return {
    ...state,
    filteredModels,
    filterValue: value,
    inputSource: 1
  };
};

const handleHeaderContextMenu = state => {
  return state;
};

const computeRowIndex = (relativeY, headerHeight, rowHeight) => {
  // Check if click is in the header area
  if (relativeY < headerHeight) {
    return -1;
  }

  // Calculate the row index (subtract header height first)
  const rowIndex = Math.floor((relativeY - headerHeight) / rowHeight);
  return rowIndex;
};

const show2 = async (uid, menuId, x, y, args) => {
  await showContextMenu2(uid, menuId, x, y, args);
};

const handleTableContextMenu = async (state, x, y) => {
  const {
    filteredModels,
    headerHeight,
    rowHeight,
    y: stateY
  } = state;

  // Calculate relative Y position from the top of the table
  const relativeY = y - stateY;

  // Calculate the row index
  const rowIndex = computeRowIndex(relativeY, headerHeight, rowHeight);

  // Get the model at the computed row index
  const model = filteredModels[rowIndex];
  if (!model) {
    return state;
  }
  await show2(state.uid, 96, x, y, {
    menuId: 96,
    modelId: model.id
  });
  return state;
};

const selectTableRow = (state, index) => {
  const {
    filteredModels,
    models
  } = state;

  // Check if the index is valid
  if (index < 0 || index >= filteredModels.length) {
    return state;
  }

  // Get the clicked model
  const clickedModel = filteredModels[index];

  // Guard against undefined clickedModel
  if (!clickedModel) {
    return state;
  }

  // Update models - toggle selection on clicked model, clear others
  const updatedModels = models.map(model => ({
    ...model,
    selected: model.id === clickedModel.id ? !model.selected : false
  }));

  // Update filtered models
  const updatedFilteredModels = filteredModels.map(model => ({
    ...model,
    selected: model.id === clickedModel.id ? !model.selected : false
  }));
  return {
    ...state,
    filteredModels: updatedFilteredModels,
    models: updatedModels
  };
};

const handleTableRowClick = (state, button, x, y) => {
  // Only handle left clicks
  if (button !== 0) {
    return state;
  }
  const {
    headerHeight,
    rowHeight,
    y: stateY
  } = state;

  // Calculate relative Y position from the top of the table
  const relativeY = y - stateY;

  // Calculate the row index
  const rowIndex = computeRowIndex(relativeY, headerHeight, rowHeight);

  // Select the row
  return selectTableRow(state, rowIndex);
};

const getModels = async () => {
  return [{
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'gpt-4',
    inputContextSize: 8192,
    name: 'GPT-4',
    outputContextSize: 4096,
    provider: 'openai',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'gpt-4-turbo',
    inputContextSize: 128_000,
    name: 'GPT-4 Turbo',
    outputContextSize: 4096,
    provider: 'openai',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'gpt-3.5-turbo',
    inputContextSize: 16_385,
    name: 'GPT-3.5 Turbo',
    outputContextSize: 4096,
    provider: 'openai',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'claude-3-opus',
    inputContextSize: 200_000,
    name: 'Claude 3 Opus',
    outputContextSize: 4096,
    provider: 'anthropic',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'claude-3-sonnet',
    inputContextSize: 200_000,
    name: 'Claude 3 Sonnet',
    outputContextSize: 4096,
    provider: 'anthropic',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'claude-3-haiku',
    inputContextSize: 200_000,
    name: 'Claude 3 Haiku',
    outputContextSize: 4096,
    provider: 'anthropic',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'claude-2.1',
    inputContextSize: 200_000,
    name: 'Claude 2.1',
    outputContextSize: 4096,
    provider: 'anthropic',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'llama-2',
    inputContextSize: 4096,
    name: 'Llama 2',
    outputContextSize: 2048,
    provider: 'meta',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'llama-2-70b',
    inputContextSize: 4096,
    name: 'Llama 2 70B',
    outputContextSize: 2048,
    provider: 'meta',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'llama-3',
    inputContextSize: 8192,
    name: 'Llama 3',
    outputContextSize: 4096,
    provider: 'meta',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: false,
    id: 'mistral-7b',
    inputContextSize: 8192,
    name: 'Mistral 7B',
    outputContextSize: 4096,
    provider: 'mistral',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'mistral-medium',
    inputContextSize: 32_768,
    name: 'Mistral Medium',
    outputContextSize: 8192,
    provider: 'mistral',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'mistral-large',
    inputContextSize: 32_768,
    name: 'Mistral Large',
    outputContextSize: 8192,
    provider: 'mistral',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'gemini-pro',
    inputContextSize: 32_768,
    name: 'Gemini Pro',
    outputContextSize: 2048,
    provider: 'google',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: true
    },
    deprecated: false,
    enabled: true,
    id: 'gemini-ultra',
    inputContextSize: 32_768,
    name: 'Gemini Ultra',
    outputContextSize: 2048,
    provider: 'google',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: false,
    id: 'palm-2',
    inputContextSize: 8192,
    name: 'PaLM 2',
    outputContextSize: 1024,
    provider: 'google',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'codellama-34b',
    inputContextSize: 16_384,
    name: 'CodeLlama 34B',
    outputContextSize: 4096,
    provider: 'meta',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'deepseek-coder',
    inputContextSize: 16_384,
    name: 'DeepSeek Coder',
    outputContextSize: 4096,
    provider: 'deepseek',
    selected: false
  }, {
    capabilities: {
      tools: false,
      vision: false
    },
    deprecated: false,
    enabled: false,
    id: 'falcon-180b',
    inputContextSize: 2048,
    name: 'Falcon 180B',
    outputContextSize: 2048,
    provider: 'tii',
    selected: false
  }, {
    capabilities: {
      tools: true,
      vision: false
    },
    deprecated: false,
    enabled: true,
    id: 'mixtral-8x7b',
    inputContextSize: 32_768,
    name: 'Mixtral 8x7B',
    outputContextSize: 8192,
    provider: 'mistral',
    selected: false
  }];
};

const isObject = value => {
  return typeof value === 'object' && value !== null;
};
const isString = value => {
  return typeof value === 'string';
};
const restoreState = (state, savedState) => {
  if (!isObject(savedState)) {
    return state;
  }
  const updates = {};

  // Try to restore filter value
  if ('filterValue' in savedState && isString(savedState.filterValue)) {
    updates.filterValue = savedState.filterValue;
  }
  return {
    ...state,
    ...updates
  };
};

const loadContent = async (state, savedState) => {
  const models = await getModels();

  // Load disabled models from cache
  const disabledModelIds = await getDisabledModels(state.cacheName, state.cacheKey);

  // Apply cached disabled state to models
  const modelsWithCachedState = models.map(model => ({
    ...model,
    enabled: !disabledModelIds.includes(model.id)
  }));
  let newState = {
    ...state,
    filteredModels: modelsWithCachedState,
    initial: false,
    models: modelsWithCachedState
  };
  if (savedState) {
    newState = restoreState(newState, savedState);
    // If a filter value was restored, apply filtering to the models
    if (newState.filterValue) {
      newState = {
        ...newState,
        filteredModels: getFilteredModels(newState.models, newState.filterValue)
      };
    }
  }
  return newState;
};

const renderCss = (oldState, newState) => {
  const css = `--header-height: ${newState.headerHeight}px; --row-height: ${newState.rowHeight}px;`;
  return [SetCss, newState.uid, css];
};

const renderFilterValue = (oldState, newState) => {
  return [SetValueByName, newState.uid, 'LanguageModelsFilter', newState.filterValue];
};

const renderFocus = (oldState, newState) => {
  if (newState.focused === 'none') {
    return [];
  }
  const elementName = newState.focused === 'input' ? 'LanguageModelsFilter' : 'LanguageModelsTable';
  return [FocusElementByName, newState.uid, elementName];
};

const SetText = 1;
const Replace = 2;
const SetAttribute = 3;
const RemoveAttribute = 4;
const Add = 6;
const NavigateChild = 7;
const NavigateParent = 8;
const RemoveChild = 9;
const NavigateSibling = 10;
const SetReferenceNodeUid = 11;

const isKey = key => {
  return key !== 'type' && key !== 'childCount';
};

const getKeys = node => {
  const keys = Object.keys(node).filter(isKey);
  return keys;
};

const arrayToTree = nodes => {
  const result = [];
  let i = 0;
  while (i < nodes.length) {
    const node = nodes[i];
    const {
      children,
      nodesConsumed
    } = getChildrenWithCount(nodes, i + 1, node.childCount || 0);
    result.push({
      node,
      children
    });
    i += 1 + nodesConsumed;
  }
  return result;
};
const getChildrenWithCount = (nodes, startIndex, childCount) => {
  if (childCount === 0) {
    return {
      children: [],
      nodesConsumed: 0
    };
  }
  const children = [];
  let i = startIndex;
  let remaining = childCount;
  let totalConsumed = 0;
  while (remaining > 0 && i < nodes.length) {
    const node = nodes[i];
    const nodeChildCount = node.childCount || 0;
    const {
      children: nodeChildren,
      nodesConsumed
    } = getChildrenWithCount(nodes, i + 1, nodeChildCount);
    children.push({
      node,
      children: nodeChildren
    });
    const nodeSize = 1 + nodesConsumed;
    i += nodeSize;
    totalConsumed += nodeSize;
    remaining--;
  }
  return {
    children,
    nodesConsumed: totalConsumed
  };
};

const compareNodes = (oldNode, newNode) => {
  const patches = [];
  // Check if node type changed - return null to signal incompatible nodes
  // (caller should handle this with a Replace operation)
  if (oldNode.type !== newNode.type) {
    return null;
  }
  // Handle reference nodes - special handling for uid changes
  if (oldNode.type === Reference) {
    if (oldNode.uid !== newNode.uid) {
      patches.push({
        type: SetReferenceNodeUid,
        uid: newNode.uid
      });
    }
    return patches;
  }
  // Handle text nodes
  if (oldNode.type === Text && newNode.type === Text) {
    if (oldNode.text !== newNode.text) {
      patches.push({
        type: SetText,
        value: newNode.text
      });
    }
    return patches;
  }
  // Compare attributes
  const oldKeys = getKeys(oldNode);
  const newKeys = getKeys(newNode);
  // Check for attribute changes
  for (const key of newKeys) {
    if (oldNode[key] !== newNode[key]) {
      patches.push({
        type: SetAttribute,
        key,
        value: newNode[key]
      });
    }
  }
  // Check for removed attributes
  for (const key of oldKeys) {
    if (!(key in newNode)) {
      patches.push({
        type: RemoveAttribute,
        key
      });
    }
  }
  return patches;
};

const treeToArray = node => {
  const result = [node.node];
  for (const child of node.children) {
    result.push(...treeToArray(child));
  }
  return result;
};

const diffChildren = (oldChildren, newChildren, patches) => {
  const maxLength = Math.max(oldChildren.length, newChildren.length);
  // Track where we are: -1 means at parent, >= 0 means at child index
  let currentChildIndex = -1;
  // Collect indices of children to remove (we'll add these patches at the end in reverse order)
  const indicesToRemove = [];
  for (let i = 0; i < maxLength; i++) {
    const oldNode = oldChildren[i];
    const newNode = newChildren[i];
    if (!oldNode && !newNode) {
      continue;
    }
    if (!oldNode) {
      // Add new node - we should be at the parent
      if (currentChildIndex >= 0) {
        // Navigate back to parent
        patches.push({
          type: NavigateParent
        });
        currentChildIndex = -1;
      }
      // Flatten the entire subtree so renderInternal can handle it
      const flatNodes = treeToArray(newNode);
      patches.push({
        type: Add,
        nodes: flatNodes
      });
    } else if (newNode) {
      // Compare nodes to see if we need any patches
      const nodePatches = compareNodes(oldNode.node, newNode.node);
      // If nodePatches is null, the node types are incompatible - need to replace
      if (nodePatches === null) {
        // Navigate to this child
        if (currentChildIndex === -1) {
          patches.push({
            type: NavigateChild,
            index: i
          });
          currentChildIndex = i;
        } else if (currentChildIndex !== i) {
          patches.push({
            type: NavigateSibling,
            index: i
          });
          currentChildIndex = i;
        }
        // Replace the entire subtree
        const flatNodes = treeToArray(newNode);
        patches.push({
          type: Replace,
          nodes: flatNodes
        });
        // After replace, we're at the new element (same position)
        continue;
      }
      // Check if we need to recurse into children
      const hasChildrenToCompare = oldNode.children.length > 0 || newNode.children.length > 0;
      // Only navigate to this element if we need to do something
      if (nodePatches.length > 0 || hasChildrenToCompare) {
        // Navigate to this child if not already there
        if (currentChildIndex === -1) {
          patches.push({
            type: NavigateChild,
            index: i
          });
          currentChildIndex = i;
        } else if (currentChildIndex !== i) {
          patches.push({
            type: NavigateSibling,
            index: i
          });
          currentChildIndex = i;
        }
        // Apply node patches (these apply to the current element, not children)
        if (nodePatches.length > 0) {
          patches.push(...nodePatches);
        }
        // Compare children recursively
        if (hasChildrenToCompare) {
          diffChildren(oldNode.children, newNode.children, patches);
        }
      }
    } else {
      // Remove old node - collect the index for later removal
      indicesToRemove.push(i);
    }
  }
  // Navigate back to parent if we ended at a child
  if (currentChildIndex >= 0) {
    patches.push({
      type: NavigateParent
    });
    currentChildIndex = -1;
  }
  // Add remove patches in reverse order (highest index first)
  // This ensures indices remain valid as we remove
  for (let j = indicesToRemove.length - 1; j >= 0; j--) {
    patches.push({
      type: RemoveChild,
      index: indicesToRemove[j]
    });
  }
};
const diffTrees = (oldTree, newTree, patches, path) => {
  // At the root level (path.length === 0), we're already AT the element
  // So we compare the root node directly, then compare its children
  if (path.length === 0 && oldTree.length === 1 && newTree.length === 1) {
    const oldNode = oldTree[0];
    const newNode = newTree[0];
    // Compare root nodes
    const nodePatches = compareNodes(oldNode.node, newNode.node);
    // If nodePatches is null, the root node types are incompatible - need to replace
    if (nodePatches === null) {
      const flatNodes = treeToArray(newNode);
      patches.push({
        type: Replace,
        nodes: flatNodes
      });
      return;
    }
    if (nodePatches.length > 0) {
      patches.push(...nodePatches);
    }
    // Compare children
    if (oldNode.children.length > 0 || newNode.children.length > 0) {
      diffChildren(oldNode.children, newNode.children, patches);
    }
  } else {
    // Non-root level or multiple root elements - use the regular comparison
    diffChildren(oldTree, newTree, patches);
  }
};

const removeTrailingNavigationPatches = patches => {
  // Find the last non-navigation patch
  let lastNonNavigationIndex = -1;
  for (let i = patches.length - 1; i >= 0; i--) {
    const patch = patches[i];
    if (patch.type !== NavigateChild && patch.type !== NavigateParent && patch.type !== NavigateSibling) {
      lastNonNavigationIndex = i;
      break;
    }
  }
  // Return patches up to and including the last non-navigation patch
  return lastNonNavigationIndex === -1 ? [] : patches.slice(0, lastNonNavigationIndex + 1);
};

const diffTree = (oldNodes, newNodes) => {
  // Step 1: Convert flat arrays to tree structures
  const oldTree = arrayToTree(oldNodes);
  const newTree = arrayToTree(newNodes);
  // Step 3: Compare the trees
  const patches = [];
  diffTrees(oldTree, newTree, patches, []);
  // Remove trailing navigation patches since they serve no purpose
  return removeTrailingNavigationPatches(patches);
};

const Button = 'Button';
const ButtonPrimary = 'ButtonPrimary';
const InputBox = 'InputBox';
const LanguageModels = 'LanguageModels';
const LanguageModelsFilter = 'LanguageModelsFilter';
const LanguageModelsHeader = 'LanguageModelsHeader';
const MaskIcon = 'MaskIcon';
const MaskIconClearAll = 'MaskIconClearAll';
const NoMatchingModels = 'NoMatchingModels';
const SearchField = 'SearchField';
const SearchFieldButton = 'SearchFieldButton';
const SearchFieldButtonDisabled = 'SearchFieldButtonDisabled';
const Table = 'Table';
const TableBody = 'TableBody';
const TableCell = 'TableCell';
const TableRow = 'TableRow';
const TableRowDisabled = 'TableRowDisabled';
const TableRowSelected = 'TableRowSelected';
const TableWrapper = 'TableWrapper';

const HandleBlur = 1;
const HandleContextMenu = 2;
const HandleFilterInput = 3;
const HandleFocus = 4;
const HandleMouseDown = 5;
const HandleAddModelsClick = 6;
const HandleClearClick = 8;
const HandleCheckboxChange = 9;
const HandleHeaderContextMenu = 10;

const getAddModelsButton = () => {
  return [{
    childCount: 1,
    className: `${Button} ${ButtonPrimary}`,
    name: 'AddModelsButton',
    onClick: HandleAddModelsClick,
    type: Button$2
  }, {
    text: addModels(),
    type: Text
  }];
};

const getClassName = isDisabled => {
  return SearchFieldButton + (isDisabled ? ' ' + SearchFieldButtonDisabled : '');
};
const getClearButton = filterValue => {
  const isDisabled = filterValue === '';
  return [{
    childCount: 1,
    className: getClassName(isDisabled),
    disabled: isDisabled,
    name: 'ClearButton',
    onClick: HandleClearClick,
    title: clear(),
    type: Button$2
  }, {
    className: `${MaskIcon} ${MaskIconClearAll}`,
    type: Div
  }];
};

const getFilterInput = () => {
  return {
    autocomplete: 'off',
    className: `${InputBox} ${LanguageModelsFilter}`,
    inputType: 'search',
    name: 'LanguageModelsFilter',
    onInput: HandleFilterInput,
    placeholder: filterLanguageModels(),
    type: Input
  };
};

const getLanguageModelsHeaderVirtualDom = filterValue => {
  return [{
    childCount: 2,
    className: LanguageModelsHeader,
    onContextMenu: HandleHeaderContextMenu,
    type: Div
  }, {
    childCount: 2,
    className: SearchField,
    type: Div
  }, getFilterInput(), ...getClearButton(filterValue), ...getAddModelsButton()];
};

const getCheckboxInput = model => {
  return {
    checked: model.enabled,
    inputType: 'checkbox',
    name: model.id,
    onChange: HandleCheckboxChange,
    type: Input
  };
};

const getTableRowClass = model => {
  const classes = [];
  if (model.selected) {
    classes.push(TableRowSelected);
  }
  if (!model.enabled) {
    classes.push(TableRowDisabled);
  }
  if (classes.length > 0) {
    return `${classes.join(' ')} ${TableRow}`;
  }
  return TableRow;
};

const getTableRow = model => {
  return {
    childCount: 5,
    className: getTableRowClass(model),
    type: Tr
  };
};
const td = {
  childCount: 1,
  className: TableCell,
  type: Td
};
const getModelRowVirtualDom = model => [getTableRow(model), td, getCheckboxInput(model), td, {
  text: model.id,
  type: Text
}, td, {
  text: model.name,
  type: Text
}, td, {
  text: model.provider,
  type: Text
}, td, {
  text: `in: ${model.inputContextSize}, out: ${model.outputContextSize}`,
  type: Text
}];

const getTableBody = models => {
  return {
    childCount: models.length,
    className: TableBody,
    onMouseDown: HandleMouseDown,
    type: TBody
  };
};
const getTableBodyVirtualDom = models => {
  return [getTableBody(models), ...models.flatMap(getModelRowVirtualDom)];
};

const getTableHead = () => {
  return {
    childCount: 1,
    type: THead
  };
};

const getTableHeaderCell = text => {
  return {
    childCount: 1,
    className: TableCell,
    type: Th
  };
};

const getTableHeaderRow = () => {
  return {
    childCount: 5,
    className: TableRow,
    type: Tr
  };
};

const getTableHeaderVirtualDom = filterValue => {
  return [getTableHead(), getTableHeaderRow(), getTableHeaderCell(), {
    text: '',
    type: Text
  }, getTableHeaderCell(id()), {
    text: id(),
    type: Text
  }, getTableHeaderCell(name()), {
    text: name(),
    type: Text
  }, getTableHeaderCell(provider()), {
    text: provider(),
    type: Text
  }, getTableHeaderCell(contextSize()), {
    text: contextSize(),
    type: Text
  }];
};

const getTableVirtualDom = (models, filterValue) => {
  return [{
    childCount: 1,
    className: TableWrapper,
    type: Div
  }, {
    childCount: 2,
    className: Table,
    onContextMenu: HandleContextMenu,
    type: Table$1
  }, ...getTableHeaderVirtualDom(), ...getTableBodyVirtualDom(models)];
};

const getNoMatchingModelsMessage = () => {
  return [{
    childCount: 1,
    className: NoMatchingModels,
    type: P
  }, {
    text: noMatchingModels(),
    type: Text
  }];
};
const getLanguageModelsVirtualDom = (models, filterValue) => {
  const content = models.length === 0 ? getNoMatchingModelsMessage() : getTableVirtualDom(models);
  return [{
    childCount: 2,
    className: LanguageModels,
    type: Div
  }, ...getLanguageModelsHeaderVirtualDom(filterValue), ...content];
};

const renderItems = (oldState, newState) => {
  if (newState.initial) {
    return [SetDom2, newState.uid, []];
  }
  const dom = getLanguageModelsVirtualDom(newState.filteredModels, newState.filterValue);
  return [SetDom2, newState.uid, dom];
};

const renderIncremental = (oldState, newState) => {
  const oldDom = renderItems(oldState, oldState)[2];
  const newDom = renderItems(newState, newState)[2];
  const patches = diffTree(oldDom, newDom);
  return [SetPatches, newState.uid, patches];
};

const renderInputValue = (oldState, newState) => {
  return [SetValueByName, newState.uid, 'LanguageModelsFilter', newState.filterValue];
};

const getRenderer = diffType => {
  switch (diffType) {
    case RenderCss:
      return renderCss;
    case RenderFilterValue:
      return renderFilterValue;
    case RenderFocus:
      return renderFocus;
    case RenderIncremental:
      return renderIncremental;
    case RenderInputValue:
      return renderInputValue;
    case RenderItems:
      return renderItems;
    default:
      throw new Error('unknown renderer');
  }
};

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

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

const renderEventListeners = () => {
  return [{
    name: HandleBlur,
    params: ['handleBlur']
  }, {
    name: HandleFilterInput,
    params: ['handleFilterInput', TargetValue]
  }, {
    name: HandleFocus,
    params: ['handleFocus']
  }, {
    name: HandleAddModelsClick,
    params: ['handleAddModelsClick']
  }, {
    name: HandleClearClick,
    params: ['handleClearClick']
  }, {
    name: HandleContextMenu,
    params: ['handleContextMenu', Button$1, ClientX, ClientY],
    preventDefault: true
  }, {
    name: HandleMouseDown,
    params: ['handleTableRowClick', Button$1, ClientX, ClientY],
    preventDefault: true,
    stopPropagation: true
  }, {
    name: HandleCheckboxChange,
    params: ['handleCheckboxChange', TargetName, TargetValue],
    stopPropagation: true
  }, {
    name: HandleHeaderContextMenu,
    params: ['handleHeaderContextMenu'],
    preventDefault: true
  }];
};

const resize = (state, dimensions) => {
  return {
    ...state,
    width: dimensions.width,
    x: dimensions.x,
    y: dimensions.y
  };
};

const saveState = state => {
  const {
    filterValue,
    models
  } = state;
  return {
    filterValue,
    models
  };
};

const commandMap = {
  'LanguageModels.create': create,
  'LanguageModels.diff2': diff2,
  'LanguageModels.disableModel': wrapCommand(disableModel$1),
  'LanguageModels.enableModel': wrapCommand(enableModel$1),
  'LanguageModels.getCommandIds': getCommandIds,
  'LanguageModels.getKeyBindings': getKeyBindings,
  'LanguageModels.getMenuEntries': wrapGetter(getMenuEntries),
  'LanguageModels.getMenuEntryIds': getMenuEntryIds,
  'LanguageModels.handleAddModelsClick': wrapCommand(handleAddModelsClick),
  'LanguageModels.handleCheckboxChange': wrapCommand(handleCheckboxChange),
  'LanguageModels.handleClearClick': wrapCommand(handleClearClick),
  'LanguageModels.handleContextMenu': wrapCommand(handleTableContextMenu),
  'LanguageModels.handleFilterInput': wrapCommand(handleFilterInput),
  'LanguageModels.handleHeaderContextMenu': wrapCommand(handleHeaderContextMenu),
  'LanguageModels.handleTableRowClick': wrapCommand(handleTableRowClick),
  'LanguageModels.loadContent': wrapCommand(loadContent),
  'LanguageModels.render2': render2,
  'LanguageModels.renderEventListeners': renderEventListeners,
  'LanguageModels.resize': wrapCommand(resize),
  'LanguageModels.saveState': wrapGetter(saveState),
  'LanguageModels.selectTableRow': wrapCommand(selectTableRow),
  'LanguageModels.terminate': terminate
};

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

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

main();
