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 {
    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 addListener = (emitter, type, callback) => {
  if ('addEventListener' in emitter) {
    emitter.addEventListener(type, callback);
  } else {
    emitter.on(type, callback);
  }
};
const removeListener = (emitter, type, callback) => {
  if ('removeEventListener' in emitter) {
    emitter.removeEventListener(type, callback);
  } else {
    emitter.off(type, callback);
  }
};
const getFirstEvent = (eventEmitter, eventMap) => {
  const {
    promise,
    resolve
  } = Promise.withResolvers();
  const listenerMap = Object.create(null);
  const cleanup = value => {
    for (const event of Object.keys(eventMap)) {
      removeListener(eventEmitter, event, listenerMap[event]);
    }
    resolve(value);
  };
  for (const [event, type] of Object.entries(eventMap)) {
    const listener = event => {
      cleanup({
        event,
        type
      });
    };
    addListener(eventEmitter, event, listener);
    listenerMap[event] = listener;
  }
  return promise;
};
const Message$1 = 3;
const create$5$1 = async ({
  isMessagePortOpen,
  messagePort
}) => {
  if (!isMessagePort(messagePort)) {
    throw new IpcError('port must be of type MessagePort');
  }
  if (isMessagePortOpen) {
    return messagePort;
  }
  const eventPromise = getFirstEvent(messagePort, {
    message: Message$1
  });
  messagePort.start();
  const {
    event,
    type
  } = await eventPromise;
  if (type !== Message$1) {
    throw new IpcError('Failed to wait for ipc message');
  }
  if (event.data !== readyMessage) {
    throw new IpcError('unexpected first message');
  }
  return messagePort;
};
const signal$1 = messagePort => {
  messagePort.start();
};
class IpcParentWithMessagePort extends Ipc {
  getData = getData$2;
  send(message) {
    this._rawIpc.postMessage(message);
  }
  sendAndTransfer(message) {
    const transfer = getTransferrables(message);
    this._rawIpc.postMessage(message, transfer);
  }
  dispose() {
    this._rawIpc.close();
  }
  onMessage(callback) {
    this._rawIpc.addEventListener('message', callback);
  }
  onClose(callback) {}
}
const wrap$5 = messagePort => {
  return new IpcParentWithMessagePort(messagePort);
};
const IpcParentWithMessagePort$1 = {
  __proto__: null,
  create: create$5$1,
  signal: signal$1,
  wrap: wrap$5
};

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$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 ReferenceError$1:
        return ReferenceError;
      case SyntaxError$1:
        return SyntaxError;
      case TypeError$1:
        return TypeError;
      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,
      data: error.stack,
      message: error.message
    };
  }
  return {
    code: Custom,
    data: {
      code: prettyError.code,
      codeFrame: prettyError.codeFrame,
      name: prettyError.name,
      stack: getStack(prettyError),
      type: getErrorType(prettyError)
    },
    message: prettyError.message
  };
};
const create$1$1 = (id, error) => {
  return {
    error,
    id,
    jsonrpc: Two$1
  };
};
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$8 = (message, result) => {
  return {
    id: message.id,
    jsonrpc: Two$1,
    result: result ?? null
  };
};
const getSuccessResponse = (message, result) => {
  const resultProperty = result ?? null;
  return create$8(message, resultProperty);
};
const getErrorResponseSimple = (id, error) => {
  return {
    error: {
      code: Custom,
      data: error,
      // @ts-ignore
      message: error.message
    },
    id,
    jsonrpc: Two$1
  };
};
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 {
      execute: options.execute,
      ipc: options.ipc,
      logError: options.logError || defaultLogError,
      message: options.message,
      preparePrettyError: options.preparePrettyError || defaultPreparePrettyError,
      requiresSocket: options.requiresSocket || defaultRequiresSocket,
      resolve: options.resolve || defaultResolve
    };
  }
  return {
    execute: args[2],
    ipc: args[0],
    logError: args[5],
    message: args[1],
    preparePrettyError: args[4],
    requiresSocket: args[6],
    resolve: args[3]
  };
};
const handleJsonRpcMessage = async (...args) => {
  const options = normalizeParams(args);
  const {
    execute,
    ipc,
    logError,
    message,
    preparePrettyError,
    requiresSocket,
    resolve
  } = 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 Two = '2.0';

const create$7 = (method, params) => {
  return {
    jsonrpc: Two,
    method,
    params
  };
};

const create$6 = (id, method, params) => {
  const message = {
    id,
    jsonrpc: Two,
    method,
    params
  };
  return message;
};

let id = 0;
const create$5 = () => {
  return ++id;
};

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

const invokeHelper = async (callbacks, ipc, method, params, useSendAndTransfer) => {
  const {
    id,
    promise
  } = registerPromise(callbacks);
  const message = create$6(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$7(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$4 = async ({
  commandMap,
  isMessagePortOpen = true,
  messagePort
}) => {
  // TODO create a commandMap per rpc instance
  register(commandMap);
  const rawIpc = await IpcParentWithMessagePort$1.create({
    isMessagePortOpen,
    messagePort
  });
  const ipc = IpcParentWithMessagePort$1.wrap(rawIpc);
  handleIpc(ipc);
  const rpc = createRpc(ipc);
  messagePort.start();
  return rpc;
};

const create$3 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  const {
    port1,
    port2
  } = new MessageChannel();
  await send(port1);
  return create$4({
    commandMap,
    isMessagePortOpen,
    messagePort: port2
  });
};

const createSharedLazyRpc = factory => {
  let rpcPromise;
  const getOrCreate = () => {
    if (!rpcPromise) {
      rpcPromise = factory();
    }
    return rpcPromise;
  };
  return {
    async dispose() {
      const rpc = await getOrCreate();
      await rpc.dispose();
    },
    async invoke(method, ...params) {
      const rpc = await getOrCreate();
      return rpc.invoke(method, ...params);
    },
    async invokeAndTransfer(method, ...params) {
      const rpc = await getOrCreate();
      return rpc.invokeAndTransfer(method, ...params);
    },
    async send(method, ...params) {
      const rpc = await getOrCreate();
      rpc.send(method, ...params);
    }
  };
};

const create$2 = async ({
  commandMap,
  isMessagePortOpen,
  send
}) => {
  return createSharedLazyRpc(() => {
    return create$3({
      commandMap,
      isMessagePortOpen,
      send
    });
  });
};

const create$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 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 rpcs = Object.create(null);
const set$c = (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 = 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$c(rpcId, mockRpc);
      // @ts-ignore
      mockRpc[Symbol.dispose] = () => {
        remove(rpcId);
      };
      // @ts-ignore
      return mockRpc;
    },
    set(rpc) {
      set$c(rpcId, rpc);
    }
  };
};

const {
  set: set$b
} = create(6010);

const {
  invoke: invoke$4,
  set: set$a
} = create(6008);

const {
  set: set$9
} = create(6007);

const {
  invoke: invoke$3,
  set: set$8
} = create(6009);

const ChatNetworkWorker = 6002;
const ChatStorageWorker = 6003;
const ChatToolWorker = 6005;
const ClipBoardWorker = 3400;
const IconThemeWorker = 7009;
const OpenerWorker = 4561;
const RendererWorker = 1;

const {
  set: set$7
} = create(ChatNetworkWorker);

const {
  invoke: invoke$2,
  set: set$6
} = create(ChatStorageWorker);
const appendEvent = async event => {
  return invoke$2('ChatStorage.appendEvent', event);
};
const clear = async () => {
  return invoke$2('ChatStorage.clear');
};
const deleteSession = async id => {
  return invoke$2('ChatStorage.deleteSession', id);
};
const getEvents = async sessionId => {
  return invoke$2('ChatStorage.getEvents', sessionId);
};

const {
  invoke: invoke$1,
  set: set$5
} = create(ChatToolWorker);

const {
  set: set$4
} = create(ClipBoardWorker);

const {
  set: set$3
} = create(IconThemeWorker);

const {
  set: set$2
} = create(OpenerWorker);

const {
  invoke,
  invokeAndTransfer,
  set: set$1
} = create(RendererWorker);
const sendMessagePortToClipBoardWorker$1 = async (port, rpcId) => {
  const command = 'ClipBoard.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToClipBoardWorker', port, command, rpcId);
};
const sendMessagePortToOpenerWorker$1 = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToOpenerWorker', port, command, rpcId);
};
const sendMessagePortToChatMathWorker$1 = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatMathWorker', port, command, rpcId);
};
const sendMessagePortToChatCoordinatorWorker$1 = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatCoordinatorWorker', port, command, rpcId);
};
const sendMessagePortToChatMessageParsingWorker = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatMessageParsingWorker', port, command, rpcId);
};
const sendMessagePortToAuthWorker$1 = async (port, rpcId) => {
  const command = 'HandleMessagePort.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToAuthWorker', port, command, rpcId);
};
const sendMessagePortToIconThemeWorker$1 = async (port, rpcId) => {
  const command = 'IconTheme.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToIconThemeWorker', port, command, rpcId);
};
const sendMessagePortToChatNetworkWorker = async port => {
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatNetworkWorker', port, 'HandleMessagePort.handleMessagePort');
};
const sendMessagePortToChatToolWorker = async port => {
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatToolWorker', port, 'HandleMessagePort.handleMessagePort');
};
const sendMessagePortToChatStorageWorker$1 = async (port, rpcId) => {
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToChatStorageWorker', port, 'HandleMessagePort.handleMessagePort', rpcId);
};
const sendMessagePortToTextMeasurementWorker$1 = async port => {
  const command = 'TextMeasurement.handleMessagePort';
  await invokeAndTransfer('SendMessagePortToExtensionHostWorker.sendMessagePortToTextMeasurementWorker', port, command, 0);
};
const getPreference = async key => {
  return await invoke('Preferences.get', key);
};
const measureTextBlockHeight$1 = async (actualInput, fontFamily, fontSize, lineHeightPx, width) => {
  return invoke(`MeasureTextHeight.measureTextBlockHeight`, actualInput, fontFamily, fontSize, lineHeightPx, width);
};

const {
  set
} = create(IconThemeWorker);

const getSessionLastActiveTime = session => {
  if (session.lastActiveTime) {
    return session.lastActiveTime;
  }
  return session.messages.at(-1)?.time;
};

const agentModes = ['agent', 'plan'];
const defaultAgentMode = 'agent';
const isAgentMode = value => {
  return agentModes.includes(value);
};

const isObject$1 = value => {
  return typeof value === 'object' && value !== null;
};

const getMessageRole = value => {
  if (value === 'assistant' || value === 'user') {
    return value;
  }
  return undefined;
};
const getMessageContentParts = value => {
  if (!Array.isArray(value)) {
    return undefined;
  }
  const parts = [];
  for (const part of value) {
    if (!isObject$1(part)) {
      continue;
    }
    parts.push({
      ...part
    });
  }
  return parts;
};
const getMessageTextFromContent = value => {
  if (typeof value === 'string') {
    return value;
  }
  if (!Array.isArray(value)) {
    return '';
  }
  return value.map(part => {
    if (!isObject$1(part)) {
      return '';
    }
    return typeof part.text === 'string' ? part.text : '';
  }).join('');
};
const normalizeStoredChatMessage = (value, options = {}) => {
  if (!isObject$1(value)) {
    return undefined;
  }
  const id = typeof value.id === 'string' ? value.id : options.fallbackId;
  const time = typeof value.time === 'string' ? value.time : options.fallbackTime;
  const role = getMessageRole(value.role);
  if (!id || !time || !role) {
    return undefined;
  }
  const content = getMessageContentParts(value.content);
  const derivedText = getMessageTextFromContent(value.content);
  const text = derivedText || (typeof value.text === 'string' ? value.text : '');
  const agentMode = typeof value.agentMode === 'string' && isAgentMode(value.agentMode) ? value.agentMode : undefined;
  const attachments = Array.isArray(value.attachments) ? value.attachments : undefined;
  const inProgress = typeof value.inProgress === 'boolean' ? value.inProgress : undefined;
  const toolCalls = Array.isArray(value.toolCalls) ? value.toolCalls : undefined;
  return {
    ...(agentMode && {
      agentMode
    }),
    ...(attachments && {
      attachments
    }),
    ...(content && {
      content
    }),
    id,
    ...(inProgress !== undefined && {
      inProgress
    }),
    role,
    text,
    time,
    ...(toolCalls && {
      toolCalls
    })
  };
};
const normalizeStoredChatMessages = messages => {
  return messages.map(message => normalizeStoredChatMessage(message)).filter(message => !!message);
};

const rpcIdViewModel = 9999;
const resetChatSessionStorage = () => {
  // no-op: chat session storage always goes through ChatStorageWorker
};
const listChatSessions = async () => {
  const sessions = await invoke$2('ChatStorage.listSessions');
  return sessions.map(session => {
    const lastActiveTime = getSessionLastActiveTime(session);
    const summary = {
      ...(session.branchName && {
        branchName: session.branchName
      }),
      id: session.id,
      ...(lastActiveTime && {
        lastActiveTime
      }),
      messages: [],
      ...(session.pullRequestUrl && {
        pullRequestUrl: session.pullRequestUrl
      }),
      ...(session.status && {
        status: session.status
      }),
      title: session.title,
      ...(session.workspaceUri && {
        workspaceUri: session.workspaceUri
      })
    };
    if (!session.projectId) {
      return summary;
    }
    return {
      ...summary,
      projectId: session.projectId
    };
  });
};
const getChatSession = async id => {
  const session = await invoke$2('ChatStorage.getSession', id);
  if (!session) {
    return undefined;
  }
  const lastActiveTime = getSessionLastActiveTime(session);
  const resultBase = {
    ...(session.branchName && {
      branchName: session.branchName
    }),
    id: session.id,
    ...(lastActiveTime && {
      lastActiveTime
    }),
    messages: normalizeStoredChatMessages(session.messages),
    ...(session.pullRequestUrl && {
      pullRequestUrl: session.pullRequestUrl
    }),
    ...(session.status && {
      status: session.status
    }),
    title: session.title,
    ...(session.workspaceUri && {
      workspaceUri: session.workspaceUri
    })
  };
  const result = session.projectId ? {
    ...resultBase,
    projectId: session.projectId
  } : resultBase;
  return result;
};
const saveChatSession = async session => {
  const lastActiveTime = getSessionLastActiveTime(session);
  const value = {
    ...(session.branchName && {
      branchName: session.branchName
    }),
    id: session.id,
    ...(lastActiveTime && {
      lastActiveTime
    }),
    messages: [...session.messages],
    ...(session.pullRequestUrl && {
      pullRequestUrl: session.pullRequestUrl
    }),
    ...(session.status && {
      status: session.status
    }),
    title: session.title,
    ...(session.workspaceUri && {
      workspaceUri: session.workspaceUri
    })
  };
  const sessionValue = session.projectId ? {
    ...value,
    projectId: session.projectId
  } : value;
  await invoke$2('ChatStorage.setSession', sessionValue);
};
const deleteChatSession = async id => {
  await deleteSession(id);
};
const clearChatSessions = async () => {
  await clear();
};
const appendChatViewEvent = async event => {
  // The local chat-view event union may temporarily grow ahead of the shared rpc-registry typings.
  await appendEvent(event);
};
const getChatViewEvents = async sessionId => {
  return getEvents(sessionId);
};
const subscribeSessionUpdates = async (uid, sessionId) => {
  await invoke$2('ChatStorage.subscribeSessionUpdates', {
    rpcId: rpcIdViewModel,
    sessionId,
    type: 'session',
    uid
  });
};

const states = new Map();
const subscriptions = new Map();
const getState = uid => {
  return states.get(uid);
};
const setState = (uid, state) => {
  states.set(uid, state);
};
const getSubscribedSessionId = uid => {
  return subscriptions.get(uid);
};
const setSubscribedSessionId = (uid, sessionId) => {
  subscriptions.set(uid, sessionId);
};

const getNextState$2 = state => {
  return {
    ...state,
    composerAttachments: [],
    composerAttachmentsHeight: 0,
    lastNormalViewMode: 'list',
    renamingSessionId: '',
    viewMode: 'list'
  };
};
const handleClickBack = async state => {
  const nextState = getNextState$2(state);
  setState(state.uid, nextState);
  await invoke('Chat.rerenderWithQuery', state.uid);
  return nextState;
};

const composerAttachmentGap = 8;
const composerAttachmentMarginBottom = 8;
const composerAttachmentFontSize = 12;
const composerAttachmentHorizontalPadding = 20;
const composerAttachmentBorderWidth = 2;
const composerAttachmentHeight = 26;
const averageCharacterWidth = composerAttachmentFontSize * 0.6;
const fallbackComposerWidth = 480;
const getComposerWidth$1 = width => {
  return Math.max(1, width - 32);
};
const getAttachmentContainerWidth = width => {
  if (width <= 0) {
    return fallbackComposerWidth;
  }
  return Math.max(1, getComposerWidth$1(width));
};
const getComposerAttachmentLabel = displayType => {
  switch (displayType) {
    case 'file':
      return 'File';
    case 'image':
      return 'Image';
    case 'invalid-image':
      return 'Invalid image';
    case 'text-file':
      return 'Text file';
    default:
      return displayType;
  }
};
const getComposerAttachmentWidth = (attachment, containerWidth) => {
  const label = `${getComposerAttachmentLabel(attachment.displayType)} · ${attachment.name}`;
  const estimatedWidth = Math.ceil(label.length * averageCharacterWidth) + composerAttachmentHorizontalPadding + composerAttachmentBorderWidth;
  return Math.min(containerWidth, estimatedWidth);
};
const getComposerAttachmentsHeight = (composerAttachments, width) => {
  if (composerAttachments.length === 0) {
    return 0;
  }
  const containerWidth = getAttachmentContainerWidth(width);
  let currentRowWidth = 0;
  let rowCount = 1;
  for (const attachment of composerAttachments) {
    const attachmentWidth = getComposerAttachmentWidth(attachment, containerWidth);
    const nextRowWidth = currentRowWidth === 0 ? attachmentWidth : currentRowWidth + composerAttachmentGap + attachmentWidth;
    if (currentRowWidth > 0 && nextRowWidth > containerWidth) {
      rowCount++;
      currentRowWidth = attachmentWidth;
    } else {
      currentRowWidth = nextRowWidth;
    }
  }
  return rowCount * composerAttachmentHeight + (rowCount - 1) * composerAttachmentGap + composerAttachmentMarginBottom;
};

const modelPickerItemHeight = 28;
const getModelPickerHeight = (modelPickerHeaderHeight, itemCount) => {
  const effectiveItemCount = Math.max(itemCount, 1);
  return Math.min(240, modelPickerHeaderHeight + effectiveItemCount * modelPickerItemHeight);
};

const getModelLabel = model => {
  if (model.provider === 'openRouter') {
    return `${model.name} (OpenRouter)`;
  }
  if (model.provider === 'openApi' || model.provider === 'openAI' || model.provider === 'openai') {
    return `${model.name} (OpenAI)`;
  }
  return model.name;
};
const getVisibleModels = (models, modelPickerSearchValue) => {
  const normalizedSearch = modelPickerSearchValue.trim().toLowerCase();
  if (!normalizedSearch) {
    return models;
  }
  return models.filter(model => getModelLabel(model).toLowerCase().includes(normalizedSearch));
};

const Composer = 'composer';
const Search = 'search';
const ModelPickerSearch = 'model-picker-search';
const OpenApiApiKeyInput = 'open-api-api-key';
const OpenRouterApiKeyInput = 'open-router-api-key';
const estimateComposerHeight = (value, lineHeight) => {
  const lineCount = value.split('\n').length;
  return lineCount * lineHeight + 8;
};
const getComposerWidth = width => {
  return Math.max(1, width - 32);
};
const getMaxComposerHeight = (lineHeight, maxComposerRows) => {
  return lineHeight * Math.max(1, maxComposerRows) + 8;
};
const getMinComposerHeight = lineHeight => {
  return lineHeight + 8;
};
const measureTextBlockHeight = async (text, fontFamily, fontSize, lineHeight, width) => {
  return measureTextBlockHeight$1(text, fontFamily, fontSize, lineHeight, width);
};
const getComposerHeight = async (state, value, width = state.width) => {
  const {
    composerFontFamily,
    composerFontSize,
    composerLineHeight,
    maxComposerRows
  } = state;
  if (value === '') {
    return composerLineHeight;
  }
  const minimumHeight = getMinComposerHeight(composerLineHeight);
  const maximumHeight = getMaxComposerHeight(composerLineHeight, maxComposerRows);
  const content = value || ' ';
  const composerWidth = getComposerWidth(width);
  try {
    const measuredHeight = await measureTextBlockHeight(content, composerFontFamily, composerFontSize, `${composerLineHeight}px`, composerWidth);
    const height = Math.ceil(measuredHeight);
    return Math.max(minimumHeight, Math.min(maximumHeight, height));
  } catch {
    return Math.max(minimumHeight, Math.min(maximumHeight, estimateComposerHeight(value, composerLineHeight)));
  }
};
const rerender = async uid => {
  await invoke('Chat.rerenderWithQuery', uid);
};
const getNextState$1 = async (state, name, value, inputSource = 'user') => {
  if (name === OpenApiApiKeyInput) {
    return {
      ...state,
      openApiApiKeyInput: value
    };
  }
  if (name === OpenRouterApiKeyInput) {
    return {
      ...state,
      openRouterApiKeyInput: value
    };
  }
  if (name === Search) {
    return {
      ...state,
      searchValue: value
    };
  }
  if (name === ModelPickerSearch) {
    const visibleModels = getVisibleModels(state.models, value);
    const selectedModelId = visibleModels.some(model => model.id === state.selectedModelId) ? state.selectedModelId : visibleModels[0]?.id || state.selectedModelId;
    return {
      ...state,
      modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, visibleModels.length),
      modelPickerListScrollTop: 0,
      modelPickerSearchValue: value,
      selectedModelId,
      visibleModels
    };
  }
  if (name !== Composer) {
    return state;
  }
  const composerHeight = await getComposerHeight(state, value);
  const composerAttachmentsHeight = getComposerAttachmentsHeight(state.composerAttachments, state.width);
  const chatInputHistoryDraft = state.chatInputHistoryIndex === -1 ? value : state.chatInputHistoryDraft;
  return {
    ...state,
    chatInputHistoryDraft,
    composerAttachmentsHeight,
    composerHeight,
    composerSelectionEnd: value.length,
    composerSelectionStart: value.length,
    composerValue: value,
    inputSource
  };
};
const handleInput = async (state, name, value, inputSource = 'user') => {
  // if (name === Composer && state.selectedSessionId) {
  //   await appendChatViewEvent({
  //     sessionId: state.selectedSessionId,
  //     timestamp: new Date().toISOString(),
  //     type: 'handle-input',
  //     value,
  //   })
  // }
  const nextState = await getNextState$1(state, name, value, inputSource);
  setState(state.uid, nextState);
  await rerender(state.uid);
  return nextState;
};

const handleMessagePort = async port => {
  await create$4({
    commandMap,
    isMessagePortOpen: true,
    messagePort: port
  });
};

const getObjectProperty = (value, key) => {
  if (!isObject$1(value)) {
    return undefined;
  }
  return value[key];
};

let nextRefreshResponse;
const setNextRefreshResponse = response => {
  nextRefreshResponse = response;
};
const hasPendingMockRefreshResponse = () => {
  return !!nextRefreshResponse;
};
const consumeNextRefreshResponse = async () => {
  if (!nextRefreshResponse) {
    return undefined;
  }
  const response = nextRefreshResponse;
  nextRefreshResponse = undefined;
  if (response.delay > 0) {
    await new Promise(resolve => setTimeout(resolve, response.delay));
  }
  if (response.type === 'error') {
    throw new Error(response.message);
  }
  return response.response;
};

const getLoggedOutBackendAuthState = (authErrorMessage = '') => {
  return {
    authAccessToken: '',
    authErrorMessage,
    userName: '',
    userState: 'loggedOut',
    userSubscriptionPlan: '',
    userUsedTokens: 0
  };
};
const getString = value => {
  return typeof value === 'string' ? value : '';
};
const getNumber = value => {
  return typeof value === 'number' ? value : 0;
};
const trimTrailingSlashes = value => {
  let trimmed = value;
  while (trimmed.endsWith('/')) {
    trimmed = trimmed.slice(0, -1);
  }
  return trimmed;
};
const getBackendRefreshUrl = backendUrl => {
  return `${trimTrailingSlashes(backendUrl)}/auth/refresh`;
};
const parseBackendAuthResponse = value => {
  if (!isObject$1(value)) {
    return getLoggedOutBackendAuthState('Backend returned an invalid authentication response.');
  }
  const authAccessToken = getString(getObjectProperty(value, 'accessToken'));
  const authErrorMessage = getString(getObjectProperty(value, 'error'));
  const userName = getString(getObjectProperty(value, 'userName'));
  const userSubscriptionPlan = getString(getObjectProperty(value, 'subscriptionPlan'));
  const userUsedTokens = getNumber(getObjectProperty(value, 'usedTokens'));
  return {
    authAccessToken,
    authErrorMessage,
    userName,
    userState: authAccessToken ? 'loggedIn' : 'loggedOut',
    userSubscriptionPlan,
    userUsedTokens
  };
};
const syncBackendAuth = async backendUrl => {
  if (!backendUrl) {
    return getLoggedOutBackendAuthState('Backend URL is missing.');
  }
  try {
    if (hasPendingMockRefreshResponse()) {
      const mockResponse = await consumeNextRefreshResponse();
      return parseBackendAuthResponse(mockResponse);
    }
    const response = await fetch(getBackendRefreshUrl(backendUrl), {
      credentials: 'include',
      headers: {
        Accept: 'application/json'
      },
      method: 'POST'
    });
    if (response.status === 401 || response.status === 403) {
      return getLoggedOutBackendAuthState();
    }
    let payload = undefined;
    try {
      payload = await response.json();
    } catch {
      payload = undefined;
    }
    if (!response.ok) {
      const parsed = parseBackendAuthResponse(payload);
      return getLoggedOutBackendAuthState(parsed.authErrorMessage || 'Backend authentication failed.');
    }
    const parsed = parseBackendAuthResponse(payload);
    if (parsed.authErrorMessage) {
      return getLoggedOutBackendAuthState(parsed.authErrorMessage);
    }
    if (!parsed.authAccessToken) {
      return getLoggedOutBackendAuthState();
    }
    return parsed;
  } catch (error) {
    const authErrorMessage = error instanceof Error && error.message ? error.message : 'Backend authentication failed.';
    return getLoggedOutBackendAuthState(authErrorMessage);
  }
};

const getTools = async () => {
  return invoke$1('ChatTool.getTools');
};

const defaultToolEnablement = {
  run_in_terminal: false
};
const parseToolEnablement = value => {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    return defaultToolEnablement;
  }
  const toolEnablement = {
    ...defaultToolEnablement
  };
  for (const [key, enabled] of Object.entries(value)) {
    if (typeof enabled === 'boolean') {
      toolEnablement[key] = enabled;
    }
  }
  return toolEnablement;
};
const isToolEnabled = (toolEnablement, toolName) => {
  if (!toolEnablement) {
    return defaultToolEnablement[toolName] ?? true;
  }
  const enabled = toolEnablement[toolName];
  if (typeof enabled === 'boolean') {
    return enabled;
  }
  return defaultToolEnablement[toolName] ?? true;
};
const filterEnabledTools = (tools, toolEnablement) => {
  return tools.filter(tool => isToolEnabled(toolEnablement, tool.function.name));
};

const readOnlyToolNames = new Set(['get_workspace_uri', 'list_file', 'list_files', 'read_file']);
const withAgentMode = (tools, agentMode) => {
  if (agentMode === 'agent') {
    return tools;
  }
  return tools.filter(tool => readOnlyToolNames.has(tool.function.name));
};
const getBasicChatTools = async (agentMode = defaultAgentMode, toolEnablement) => {
  try {
    return withAgentMode(filterEnabledTools(await getTools(), toolEnablement), agentMode);
  } catch {
    return withAgentMode(filterEnabledTools([], toolEnablement), agentMode);
  }
};

const createNewSession = async (title = '') => {
  const sessionId = crypto.randomUUID();
  const date = new Date();
  const timestamp = date.toISOString();
  await invoke$4('ChatCoordinator.createSession', {
    sessionId,
    timestamp,
    title
  });
  return sessionId;
};

const ensureSubscribed = async (uid, sessionId) => {
  if (getSubscribedSessionId(uid) === sessionId) {
    return;
  }
  await subscribeSessionUpdates(uid, sessionId);
  setSubscribedSessionId(uid, sessionId);
};

const getAuthAccessToken = state => {
  const authAccessToken = getObjectProperty(state, 'authAccessToken');
  return typeof authAccessToken === 'string' ? authAccessToken : '';
};

const getBackendUrl = state => {
  const backendUrl = getObjectProperty(state, 'backendUrl');
  return typeof backendUrl === 'string' ? backendUrl : '';
};

const getComposerAttachments$1 = state => {
  const {
    composerAttachments
  } = state;
  return Array.isArray(composerAttachments) ? composerAttachments : [];
};

const useMockApiEnabled = state => {
  return getObjectProperty(state, 'useMockApi') === true;
};

const getCoordinatorModelId = state => {
  return useMockApiEnabled(state) ? 'test' : state.selectedModelId;
};

const getNextChatInputHistory = (chatInputHistory, userText) => {
  return chatInputHistory.at(-1) === userText ? chatInputHistory : [...chatInputHistory, userText];
};

const useOwnBackendEnabled = state => {
  return getObjectProperty(state, 'useOwnBackend') === true;
};

// const handleSubmitWithExistingSession

const defaultMaxToolCalls = 100;
const getNewSessionTitle = userText => {
  return userText.slice(0, 30);
};
const handleRpcSubmit = async state => {
  const {
    agentMode = 'agent',
    chatInputHistory,
    composerValue,
    openApiApiKey,
    selectedModelId,
    selectedSessionId,
    sessions,
    systemPrompt,
    toolEnablement,
    uid,
    viewMode
  } = state;
  const effectiveAgentMode = agentMode === 'plan' ? 'plan' : 'agent';
  const effectiveToolEnablement = parseToolEnablement(toolEnablement);
  const userText = composerValue.trim();
  if (!userText) {
    return;
  }
  const shouldCreateNewSession = !selectedSessionId || viewMode === 'list';

  // TODO there is a race condition when the user submits another query
  // while the session is being created
  let actualSessionId = selectedSessionId;
  const title = getNewSessionTitle(userText);
  if (shouldCreateNewSession) {
    actualSessionId = await createNewSession(title);
  }
  const newSessions = shouldCreateNewSession ? [...sessions, {
    id: actualSessionId,
    messages: [],
    // TODO remove this from here
    title
  }] : sessions;
  const nextState = {
    ...state,
    chatInputHistory: getNextChatInputHistory(chatInputHistory, userText),
    chatInputHistoryIndex: -1,
    composerValue: '',
    focus: 'composer',
    focused: true,
    lastSubmittedSessionId: actualSessionId,
    messages: shouldCreateNewSession ? [] : state.messages,
    selectedSessionId: actualSessionId,
    sessions: newSessions,
    viewMode: 'detail'
  };
  setState(uid, nextState);
  await ensureSubscribed(uid, actualSessionId);
  const backendUrl = getBackendUrl(nextState);
  const shouldSyncBackendAuth = !!backendUrl && (useOwnBackendEnabled(nextState) || !!getAuthAccessToken(nextState));
  const authState = shouldSyncBackendAuth ? await syncBackendAuth(backendUrl) : undefined;
  const effectiveState = authState ? {
    ...nextState,
    ...authState
  } : nextState;
  setState(uid, effectiveState);
  const coordinatorModelId = getCoordinatorModelId(state);
  const tools = await getBasicChatTools(effectiveAgentMode, effectiveToolEnablement);
  if (selectedModelId === 'test' && !useMockApiEnabled(state)) {
    await invoke$4('ChatCoordinator.registerMockResponse', {
      text: `Mock AI response: I received "${userText}".`
    });
  }
  try {
    await invoke$4('ChatCoordinator.handleSubmit', {
      attachments: getComposerAttachments$1(state),
      authAccessToken: getAuthAccessToken(state),
      backendUrl: getBackendUrl(state),
      id: crypto.randomUUID(),
      maxToolCalls: defaultMaxToolCalls,
      modelId: coordinatorModelId,
      openAiKey: openApiApiKey || '',
      requestId: crypto.randomUUID(),
      role: 'user',
      sessionId: actualSessionId,
      systemPrompt: systemPrompt,
      text: userText,
      tools,
      useOwnBackend: useOwnBackendEnabled(state)
    });
  } catch (error) {
    console.error(error);
  }
};

const emptyMessageContent = [{
  children: [{
    text: '',
    type: 'text'
  }],
  type: 'text'
}];
const getPlainTextMessageContent = text => {
  return [{
    children: [{
      text,
      type: 'text'
    }],
    type: 'text'
  }];
};
const isMessageLike = value => {
  return isObject$1(value) && typeof value.id === 'string' && typeof value.text === 'string';
};
const setParsedMessageContent = (parsedMessages, messageId, text, parsedContent) => {
  const index = parsedMessages.findIndex(item => item.id === messageId);
  if (index === -1) {
    return [...parsedMessages, {
      id: messageId,
      parsedContent,
      text
    }];
  }
  const existingItem = parsedMessages[index];
  if (existingItem.text === text && existingItem.parsedContent === parsedContent) {
    return parsedMessages;
  }
  return [...parsedMessages.slice(0, index), {
    ...existingItem,
    parsedContent,
    text
  }, ...parsedMessages.slice(index + 1)];
};
const getMessagesNeedingParsing = (parsedMessages, messages) => {
  return messages.filter(message => {
    const existingParsedMessage = parsedMessages.find(item => item.id === message.id);
    return !existingParsedMessage || existingParsedMessage.text !== message.text;
  });
};
const parseAndStoreMessagesContent = async (parsedMessages, messages) => {
  const typedMessages = messages.filter(isMessageLike);
  const messagesNeedingParsing = getMessagesNeedingParsing(parsedMessages, typedMessages);
  if (messagesNeedingParsing.length === 0) {
    return parsedMessages;
  }
  let parsedContents;
  try {
    parsedContents = await invoke$3('ChatMessageParsing.parseMessageContents', messagesNeedingParsing.map(message => message.text));
  } catch {
    parsedContents = messagesNeedingParsing.map(message => getPlainTextMessageContent(message.text));
  }
  let nextParsedMessages = parsedMessages;
  for (const [index, message] of messagesNeedingParsing.entries()) {
    const parsedContent = message.text === '' ? emptyMessageContent : parsedContents[index];
    nextParsedMessages = setParsedMessageContent(nextParsedMessages, message.id, message.text, parsedContent);
  }
  return nextParsedMessages;
};

const handleStorageUpdateListMode = async state => {
  // TODO update / requery list
  return state;
};
const getMessageFromEvent = event => {
  if (!isObject$1(event) || typeof event.type !== 'string') {
    return undefined;
  }
  if (event.type === 'chat-message-added') {
    const fallbackTime = typeof event.timestamp === 'string' ? event.timestamp : undefined;
    return normalizeStoredChatMessage(event.message, {
      ...(fallbackTime && {
        fallbackTime
      })
    });
  }
  if (event.type === 'message') {
    const fallbackId = typeof event.requestId === 'string' ? event.requestId : undefined;
    const fallbackTime = typeof event.timestamp === 'string' ? event.timestamp : undefined;
    return normalizeStoredChatMessage(event.message, {
      ...(fallbackId && {
        fallbackId
      }),
      ...(fallbackTime && {
        fallbackTime
      })
    });
  }
  return undefined;
};
const toMessages = events => {
  const messages = [];
  for (const event of events) {
    const message = getMessageFromEvent(event);
    if (message) {
      messages.push(message);
    }
  }
  return messages;
};
const getNewSessions = state => {
  const newSessions = state.sessions.map(session => {
    if (session.id === state.selectedSessionId) {
      return {
        ...session,
        messages: []
      };
    }
    return session;
  });
  if (newSessions.length === 0) {
    return [{
      id: state.selectedSessionId,
      messages: []
    }];
  }
  return newSessions;
};
const handleStorageUpdateDetailMode = async state => {
  // TODO requery messages
  const {
    selectedSessionId
  } = state;
  const events = await invoke$2('ChatStorage.getMessages', selectedSessionId);
  const messages = toMessages(events);
  const parsedMessages = await parseAndStoreMessagesContent([], messages);
  const newSessions = getNewSessions(state);
  return {
    ...state,
    messages,
    parsedMessages,
    sessions: newSessions
  };
};
const getNextState = async state => {
  if (state.viewMode === 'detail') {
    return handleStorageUpdateDetailMode(state);
  }
  return handleStorageUpdateListMode(state);
};
const handleChatStorageUpdate = async (uid, sessionId) => {
  const state = getState(uid);
  if (!state) {
    return;
  }
  const nextState = await getNextState(state);
  setState(uid, nextState);
  await invoke('Chat.rerenderWithQuery', uid);
};

const ensureBlankProject = (projects, fallbackBlankProject) => {
  if (projects.some(project => project.name === '_blank')) {
    return projects;
  }
  return [fallbackBlankProject, ...projects];
};

const isChatAttachmentAddedEvent = event => {
  return event.type === 'chat-attachment-added';
};
const isChatAttachmentRemovedEvent = event => {
  return event.type === 'chat-attachment-removed';
};
const isImageFile = mimeType => {
  return mimeType.startsWith('image/');
};
const textFileRegex = /\.txt$/i;
const isTextFile = (name, mimeType) => {
  return mimeType === 'text/plain' || textFileRegex.test(name);
};
const isValidImage = async (blob, mimeType) => {
  if (mimeType.startsWith('image/') && mimeType !== 'image/png' && mimeType !== 'image/jpeg') {
    return true;
  }
  if (typeof createImageBitmap !== 'function') {
    return true;
  }
  try {
    const imageBitmap = await createImageBitmap(blob);
    imageBitmap.close();
    return true;
  } catch {
    return false;
  }
};
const getComposerAttachmentDisplayType = async (blob, name, mimeType) => {
  if (isImageFile(mimeType)) {
    return (await isValidImage(blob, mimeType)) ? 'image' : 'invalid-image';
  }
  if (isTextFile(name, mimeType)) {
    return 'text-file';
  }
  return 'file';
};
const chunkSize = 0x80_00;
const getBlobBase64 = async blob => {
  const bytes = new Uint8Array(await blob.arrayBuffer());
  if (typeof Buffer !== 'undefined') {
    return Buffer.from(bytes).toString('base64');
  }
  let binary = '';
  for (let i = 0; i < bytes.length; i += chunkSize) {
    binary += String.fromCodePoint(...bytes.subarray(i, i + chunkSize));
  }
  return btoa(binary);
};
const getComposerAttachmentPreviewSrc = async (blob, displayType, mimeType) => {
  if (displayType !== 'image') {
    return undefined;
  }
  const base64 = await getBlobBase64(blob);
  return `data:${mimeType || 'application/octet-stream'};base64,${base64}`;
};
const getComposerAttachmentTextContent = async (blob, displayType) => {
  if (displayType !== 'text-file') {
    return undefined;
  }
  return blob.text();
};
const getComposerAttachments = async sessionId => {
  if (!sessionId) {
    return [];
  }
  const events = await getChatViewEvents(sessionId);
  const attachments = new Map();
  for (const event of events) {
    if (isChatAttachmentRemovedEvent(event)) {
      attachments.delete(event.attachmentId);
      continue;
    }
    if (!isChatAttachmentAddedEvent(event)) {
      continue;
    }
    const displayType = await getComposerAttachmentDisplayType(event.blob, event.name, event.mimeType);
    const [previewSrc, textContent] = await Promise.all([getComposerAttachmentPreviewSrc(event.blob, displayType, event.mimeType), getComposerAttachmentTextContent(event.blob, displayType)]);
    attachments.set(event.attachmentId, {
      attachmentId: event.attachmentId,
      displayType,
      mimeType: event.mimeType,
      name: event.name,
      ...(previewSrc && {
        previewSrc
      }),
      size: event.size,
      ...(typeof textContent === 'string' && {
        textContent
      })
    });
  }
  return [...attachments.values()];
};

const getSavedAgentMode = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    agentMode
  } = savedState;
  if (typeof agentMode !== 'string') {
    return undefined;
  }
  if (!isAgentMode(agentMode)) {
    return undefined;
  }
  return agentMode;
};

const getSavedChatListScrollTop = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    chatListScrollTop
  } = savedState;
  if (typeof chatListScrollTop !== 'number') {
    return undefined;
  }
  return chatListScrollTop;
};

const clampSelectionIndex = (value, max) => {
  if (!Number.isFinite(value)) {
    return 0;
  }
  const integerValue = Math.trunc(value);
  if (integerValue < 0) {
    return 0;
  }
  if (integerValue > max) {
    return max;
  }
  return integerValue;
};
const getNormalizedComposerSelection = (composerValue, composerSelectionStart, composerSelectionEnd) => {
  const maxIndex = composerValue.length;
  const normalizedStart = clampSelectionIndex(composerSelectionStart, maxIndex);
  const normalizedEnd = clampSelectionIndex(composerSelectionEnd, maxIndex);
  if (normalizedStart <= normalizedEnd) {
    return [normalizedStart, normalizedEnd];
  }
  return [normalizedEnd, normalizedStart];
};

const getSavedComposerSelection = (savedState, composerValue) => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    composerSelectionEnd,
    composerSelectionStart
  } = savedState;
  if (typeof composerSelectionStart !== 'number') {
    return undefined;
  }
  if (typeof composerSelectionEnd !== 'number') {
    return undefined;
  }
  return getNormalizedComposerSelection(composerValue, composerSelectionStart, composerSelectionEnd);
};

const getSavedComposerValue = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    composerValue
  } = savedState;
  if (typeof composerValue !== 'string') {
    return undefined;
  }
  return composerValue;
};

const getSavedLastNormalViewMode = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    lastNormalViewMode
  } = savedState;
  if (lastNormalViewMode !== 'list' && lastNormalViewMode !== 'detail') {
    return undefined;
  }
  return lastNormalViewMode;
};

const getSavedMessagesScrollTop = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    messagesScrollTop
  } = savedState;
  if (typeof messagesScrollTop !== 'number') {
    return undefined;
  }
  return messagesScrollTop;
};

const getSavedProjectExpandedIds = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    projectExpandedIds
  } = savedState;
  if (!Array.isArray(projectExpandedIds)) {
    return undefined;
  }
  const ids = projectExpandedIds.filter(id => typeof id === 'string');
  if (ids.length === 0) {
    return undefined;
  }
  return ids;
};

const getSavedProjectListScrollTop = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    projectListScrollTop
  } = savedState;
  if (typeof projectListScrollTop !== 'number') {
    return undefined;
  }
  return projectListScrollTop;
};

const getSavedProjects = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    projects
  } = savedState;
  if (!Array.isArray(projects)) {
    return undefined;
  }
  const validProjects = projects.filter(project => {
    if (!isObject$1(project)) {
      return false;
    }
    return typeof project.id === 'string' && typeof project.name === 'string' && typeof project.uri === 'string';
  });
  if (validProjects.length === 0) {
    return undefined;
  }
  return validProjects;
};

const getSavedProjectSidebarWidth = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    projectSidebarWidth
  } = savedState;
  if (typeof projectSidebarWidth !== 'number') {
    return undefined;
  }
  return projectSidebarWidth;
};

const isReasoningEffort = value => {
  return value === 'extra-high' || value === 'high' || value === 'medium' || value === 'low';
};
const getSavedReasoningEffort = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    reasoningEffort
  } = savedState;
  if (typeof reasoningEffort !== 'string') {
    return undefined;
  }
  if (!isReasoningEffort(reasoningEffort)) {
    return undefined;
  }
  return reasoningEffort;
};

const getSavedSelectedModelId = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    selectedModelId
  } = savedState;
  if (typeof selectedModelId !== 'string') {
    return undefined;
  }
  return selectedModelId;
};

const getSavedSelectedProjectId = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    selectedProjectId
  } = savedState;
  if (typeof selectedProjectId !== 'string') {
    return undefined;
  }
  return selectedProjectId;
};

const getSavedSelectedSessionId = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    selectedSessionId
  } = savedState;
  if (typeof selectedSessionId !== 'string') {
    return undefined;
  }
  return selectedSessionId;
};

const getSavedViewMode = savedState => {
  if (!isObject$1(savedState)) {
    return undefined;
  }
  const {
    viewMode
  } = savedState;
  if (viewMode !== 'list' && viewMode !== 'detail' && viewMode !== 'chat-focus') {
    return undefined;
  }
  return viewMode;
};

const getVisibleSessions = (sessions, selectedProjectId) => {
  if (!selectedProjectId) {
    return sessions;
  }
  const hasAssignedProjects = sessions.some(session => !!session.projectId);
  if (!hasAssignedProjects) {
    return sessions;
  }
  return sessions.filter(session => session.projectId === selectedProjectId);
};

const get = async key => {
  return getPreference(key);
};

const defaultBackendUrl = 'https://lvce-editor.dev';
const getBooleanPreference = async (key, fallback) => {
  try {
    const value = await get(key);
    return typeof value === 'boolean' ? value : fallback;
  } catch {
    return fallback;
  }
};
const getStringPreference = async (key, fallback = '') => {
  try {
    const value = await get(key);
    return typeof value === 'string' ? value : fallback;
  } catch {
    return fallback;
  }
};
const loadOpenApiApiKey = async () => {
  const openApiKey = await getStringPreference('secrets.openApiKey');
  if (openApiKey) {
    return openApiKey;
  }
  const openApiApiKey = await getStringPreference('secrets.openApiApiKey');
  if (openApiApiKey) {
    return openApiApiKey;
  }
  return getStringPreference('secrets.openAiApiKey');
};
const loadPreferences = async () => {
  const [aiSessionTitleGenerationEnabled, authEnabled, authUseRedirect, backendUrl, chatHistoryEnabled, composerDropEnabled, openApiApiKey, openRouterApiKey, emitStreamingFunctionCallEvents, reasoningPickerEnabled, runModePickerEnabled, scrollDownButtonEnabled, searchEnabled, showChatListTime, showModelUsageMultiplier, streamingEnabled, todoListToolEnabled, savedToolEnablement, passIncludeObfuscation, useChatCoordinatorWorker, useChatMathWorker, useChatNetworkWorkerForRequests, useChatToolWorker, useAuthWorker, useOwnBackend, voiceDictationEnabled] = await Promise.all([getBooleanPreference('chatView.aiSessionTitleGenerationEnabled', false), getBooleanPreference('chat.authEnabled', false), getBooleanPreference('chat.authUseRedirect', false), getStringPreference('chat.backendUrl', defaultBackendUrl), getBooleanPreference('chat.chatHistoryEnabled', true), getBooleanPreference('chatView.composerDropEnabled', true), loadOpenApiApiKey(), getStringPreference('secrets.openRouterApiKey', ''), getBooleanPreference('chatView.emitStreamingFunctionCallEvents', false), getBooleanPreference('chatView.reasoningPickerEnabled', false), getBooleanPreference('chatView.runModePickerEnabled', true), getBooleanPreference('chatView.scrollDownButtonEnabled', false), getBooleanPreference('chatView.searchEnabled', false), getBooleanPreference('chatView.showChatListTime', true), getBooleanPreference('chatView.showModelUsageMultiplier', true), getBooleanPreference('chatView.streamingEnabled', true), getBooleanPreference('chatView.todoListToolEnabled', false), get('chat.toolEnablement'), getBooleanPreference('chatView.passIncludeObfuscation', false), getBooleanPreference('chatView.useChatCoordinatorWorker', true), getBooleanPreference('chatView.useChatMathWorker', true), getBooleanPreference('chatView.useChatNetworkWorkerForRequests', false), getBooleanPreference('chatView.useChatToolWorker', true), getBooleanPreference('chatView.useAuthWorker', false), getBooleanPreference('chat.useOwnBackend', true), getBooleanPreference('chatView.voiceDictationEnabled', false)]);
  return {
    aiSessionTitleGenerationEnabled,
    authEnabled,
    authUseRedirect,
    backendUrl,
    chatHistoryEnabled,
    composerDropEnabled,
    emitStreamingFunctionCallEvents,
    openApiApiKey,
    openRouterApiKey,
    passIncludeObfuscation,
    reasoningPickerEnabled,
    runModePickerEnabled,
    scrollDownButtonEnabled,
    searchEnabled,
    showChatListTime,
    showModelUsageMultiplier,
    streamingEnabled,
    todoListToolEnabled,
    toolEnablement: parseToolEnablement(savedToolEnablement),
    useAuthWorker,
    useChatCoordinatorWorker,
    useChatMathWorker,
    useChatNetworkWorkerForRequests,
    useChatToolWorker,
    useOwnBackend,
    voiceDictationEnabled
  };
};

const normalizeSessionOnLoad = session => {
  if (session.status !== 'in-progress') {
    return session;
  }
  return {
    ...session,
    messages: session.messages.map(message => {
      if (!isObject$1(message) || getObjectProperty(message, 'inProgress') !== true) {
        return message;
      }
      return {
        ...message,
        inProgress: false
      };
    }),
    status: 'stopped'
  };
};
const normalizeSessionsOnLoad = sessions => {
  return sessions.map(normalizeSessionOnLoad);
};

const getSelectedSession = (sessions, selectedSessionId) => {
  return sessions.find(session => session.id === selectedSessionId);
};
const getProjectUri = (state, projectId) => {
  return state.projects.find(project => project.id === projectId)?.uri || '';
};
const getWorkspaceUri = (state, session) => {
  if (session?.workspaceUri) {
    return session.workspaceUri;
  }
  return getProjectUri(state, session?.projectId || state.selectedProjectId);
};
const withHiddenBranchPicker = state => {
  return {
    ...state,
    gitBranches: [],
    gitBranchPickerErrorMessage: '',
    gitBranchPickerOpen: false,
    gitBranchPickerVisible: false
  };
};
const toFileSystemPath = uri => {
  if (!uri.startsWith('file://')) {
    return uri;
  }
  return decodeURIComponent(new URL(uri).pathname);
};
const getRelativePath = (fromPath, toPath) => {
  if (!fromPath.startsWith('/') || !toPath.startsWith('/')) {
    return toPath;
  }
  const fromParts = fromPath.split('/').filter(Boolean);
  const toParts = toPath.split('/').filter(Boolean);
  let commonPrefixLength = 0;
  while (commonPrefixLength < fromParts.length && commonPrefixLength < toParts.length && fromParts[commonPrefixLength] === toParts[commonPrefixLength]) {
    commonPrefixLength++;
  }
  const parentSegments = fromParts.slice(commonPrefixLength).map(() => '..');
  const childSegments = toParts.slice(commonPrefixLength);
  return [...parentSegments, ...childSegments].join('/') || '.';
};
const toFileSystemTarget = (workspaceUri, uri) => {
  const workspacePath = toFileSystemPath(workspaceUri);
  const fileSystemPath = toFileSystemPath(uri);
  if (!workspaceUri.startsWith('file://') || !uri.startsWith('file://')) {
    return fileSystemPath;
  }
  return getRelativePath(workspacePath, fileSystemPath);
};
const decodeFileContent = content => {
  if (typeof content === 'string') {
    return content;
  }
  if (content instanceof Uint8Array) {
    return new TextDecoder().decode(content);
  }
  if (Array.isArray(content)) {
    return new TextDecoder().decode(new Uint8Array(content));
  }
  return '';
};
const readTextFile = async (workspaceUri, uri) => {
  const result = await invoke('FileSystem.readFile', toFileSystemTarget(workspaceUri, uri));
  return decodeFileContent(result);
};
const parseEntries = value => {
  if (!Array.isArray(value)) {
    return [];
  }
  return value.flatMap(entry => {
    if (Array.isArray(entry) && typeof entry[0] === 'string' && typeof entry[1] === 'number') {
      return [{
        name: entry[0],
        type: entry[1]
      }];
    }
    const name = getObjectProperty(entry, 'name');
    const type = getObjectProperty(entry, 'type');
    if (typeof name === 'string' && typeof type === 'number') {
      return [{
        name,
        type
      }];
    }
    return [];
  });
};
const readDir = async (workspaceUri, uri) => {
  const result = await invoke('FileSystem.readDirWithFileTypes', toFileSystemTarget(workspaceUri, uri));
  return parseEntries(result);
};
const slashAtEndRegex = /\/$/;
const toGitUri = (baseUri, ...segments) => {
  const url = new URL(baseUri.endsWith('/') ? baseUri : `${baseUri}/`);
  for (const segment of segments) {
    url.pathname = `${url.pathname.replace(slashAtEndRegex, '')}/${segment.split('/').map(part => encodeURIComponent(part)).join('/')}`;
  }
  return url.toString();
};
const headRefRegex = /^ref:\s+refs\/heads\/(.+)$/m;
const parseCurrentBranch = headContent => {
  const match = headRefRegex.exec(headContent.trim());
  if (match) {
    return match[1].trim();
  }
  return '';
};
const gitDirPointerRegex = /^gitdir:\s*(.+)$/m;
const getGitDirUri = async workspaceUri => {
  const gitUri = toGitUri(workspaceUri, '.git');
  try {
    await readTextFile(workspaceUri, toGitUri(gitUri, 'HEAD'));
    return gitUri;
  } catch {
    // Support repositories where .git is a pointer file.
  }
  const gitPointer = await readTextFile(workspaceUri, gitUri);
  const match = gitDirPointerRegex.exec(gitPointer);
  if (!match) {
    return '';
  }
  return new URL(match[1].trim(), workspaceUri.endsWith('/') ? workspaceUri : `${workspaceUri}/`).href;
};
const FileTypeFile = 1;
const FileTypeDirectory = 2;
const collectBranchNames = async (workspaceUri, refsHeadsUri, prefix) => {
  const entries = await readDir(workspaceUri, refsHeadsUri);
  const branches = [];
  for (const entry of entries) {
    if (entry.type === FileTypeDirectory) {
      branches.push(...(await collectBranchNames(workspaceUri, toGitUri(refsHeadsUri, entry.name), `${prefix}${entry.name}/`)));
      continue;
    }
    if (entry.type === FileTypeFile) {
      branches.push(`${prefix}${entry.name}`);
    }
  }
  return branches;
};
const getGitBranches = async workspaceUri => {
  const gitDirUri = await getGitDirUri(workspaceUri);
  if (!gitDirUri) {
    throw new Error('Git repository not found.');
  }
  const branches = new Set();
  let currentBranch = '';
  try {
    const headContent = await readTextFile(workspaceUri, toGitUri(gitDirUri, 'HEAD'));
    currentBranch = parseCurrentBranch(headContent);
    if (currentBranch) {
      branches.add(currentBranch);
    }
  } catch {
    // Continue and discover branches from refs.
  }
  try {
    const discoveredBranches = await collectBranchNames(workspaceUri, toGitUri(gitDirUri, 'refs', 'heads'), '');
    for (const branch of discoveredBranches) {
      branches.add(branch);
    }
  } catch {
    // Repositories without local refs should still open.
  }
  if (branches.size === 0) {
    throw new Error('No local git branches found.');
  }
  return [...branches].toSorted((a, b) => a.localeCompare(b)).map(name => ({
    current: name === currentBranch,
    name
  }));
};
const hasGitRepository = async workspaceUri => {
  try {
    return Boolean(await getGitDirUri(workspaceUri));
  } catch {
    return false;
  }
};
const refreshGitBranchPickerVisibility = async state => {
  if (state.viewMode !== 'chat-focus') {
    return {
      ...state,
      gitBranchPickerErrorMessage: '',
      gitBranchPickerOpen: false
    };
  }
  const selectedSession = getSelectedSession(state.sessions, state.selectedSessionId);
  const fallbackBranchName = selectedSession?.branchName || '';
  const workspaceUri = getWorkspaceUri(state, selectedSession);
  if (!workspaceUri) {
    return withHiddenBranchPicker(state);
  }
  const visible = await hasGitRepository(workspaceUri);
  if (!visible) {
    if (fallbackBranchName) {
      return {
        ...state,
        gitBranches: [{
          current: true,
          name: fallbackBranchName
        }],
        gitBranchPickerVisible: true
      };
    }
    return withHiddenBranchPicker(state);
  }
  try {
    const gitBranches = await getGitBranches(workspaceUri);
    return {
      ...state,
      gitBranches,
      gitBranchPickerVisible: true
    };
  } catch {
    if (fallbackBranchName) {
      return {
        ...state,
        gitBranches: [{
          current: true,
          name: fallbackBranchName
        }],
        gitBranchPickerVisible: true
      };
    }
    return {
      ...state,
      gitBranchPickerVisible: true
    };
  }
};

/* cspell:ignore sonarjs */

const loadContent = async (state, savedState) => {
  const savedSelectedModelId = getSavedSelectedModelId(savedState);
  const savedViewMode = getSavedViewMode(savedState);
  const savedComposerValue = getSavedComposerValue(savedState);
  const composerValue = savedComposerValue ?? state.composerValue;
  const savedComposerSelection = getSavedComposerSelection(savedState, composerValue);
  const [composerSelectionStart, composerSelectionEnd] = savedComposerSelection ?? [state.composerSelectionStart, state.composerSelectionEnd];
  const {
    aiSessionTitleGenerationEnabled,
    authEnabled,
    authUseRedirect,
    backendUrl,
    chatHistoryEnabled,
    composerDropEnabled,
    emitStreamingFunctionCallEvents,
    openApiApiKey,
    openRouterApiKey,
    passIncludeObfuscation,
    reasoningPickerEnabled,
    runModePickerEnabled,
    scrollDownButtonEnabled,
    searchEnabled,
    showChatListTime,
    showModelUsageMultiplier,
    streamingEnabled,
    todoListToolEnabled,
    toolEnablement,
    useAuthWorker,
    useChatCoordinatorWorker,
    useChatMathWorker,
    useChatNetworkWorkerForRequests,
    useChatToolWorker,
    useOwnBackend,
    voiceDictationEnabled
  } = await loadPreferences();
  const authState = getLoggedOutBackendAuthState();
  const storedSessions = await listChatSessions();
  let sessions = storedSessions;
  const preferredSessionId = getSavedSelectedSessionId(savedState) || state.selectedSessionId;
  const savedProjects = getSavedProjects(savedState);
  const baseProjects = savedProjects && savedProjects.length > 0 ? savedProjects : state.projects;
  const blankProject = state.projects.find(project => project.name === '_blank') || {
    id: 'project-blank',
    name: '_blank',
    uri: ''
  };
  const projects = ensureBlankProject(baseProjects, blankProject);
  const preferredProjectId = getSavedSelectedProjectId(savedState) || state.selectedProjectId;
  const agentMode = getSavedAgentMode(savedState) ?? state.agentMode;
  const selectedProjectId = projects.some(project => project.id === preferredProjectId) ? preferredProjectId : projects[0]?.id || '';
  const preferredModelId = savedSelectedModelId || state.selectedModelId;
  const chatListScrollTop = getSavedChatListScrollTop(savedState) ?? state.chatListScrollTop;
  const messagesScrollTop = getSavedMessagesScrollTop(savedState) ?? state.messagesScrollTop;
  const projectListScrollTop = getSavedProjectListScrollTop(savedState) ?? state.projectListScrollTop;
  const projectSidebarWidth = getSavedProjectSidebarWidth(savedState) ?? state.projectSidebarWidth;
  const savedProjectExpandedIds = getSavedProjectExpandedIds(savedState);
  const projectExpandedIds = (savedProjectExpandedIds || state.projectExpandedIds).filter(id => projects.some(project => project.id === id));
  const reasoningEffort = getSavedReasoningEffort(savedState) ?? state.reasoningEffort;
  const selectedModelId = state.models.some(model => model.id === preferredModelId) ? preferredModelId : state.models[0]?.id || '';
  const visibleModels = getVisibleModels(state.models, '');
  const visibleSessions = getVisibleSessions(sessions, selectedProjectId);
  const selectedSessionId = visibleSessions.some(session => session.id === preferredSessionId) ? preferredSessionId : visibleSessions[0]?.id || '';
  const loadedSession = selectedSessionId ? await getChatSession(selectedSessionId) : undefined;
  sessions = normalizeSessionsOnLoad(sessions);
  const composerAttachments = await getComposerAttachments(selectedSessionId);
  const messages = loadedSession?.messages || [];
  const parsedMessages = await parseAndStoreMessagesContent(state.parsedMessages, messages);
  const preferredViewMode = savedViewMode || state.viewMode;
  const savedLastNormalViewMode = getSavedLastNormalViewMode(savedState);
  const lastNormalViewMode = savedLastNormalViewMode || (preferredViewMode === 'detail' ? 'detail' : state.lastNormalViewMode);
  const viewMode = sessions.length === 0 || !selectedSessionId ? 'list' : preferredViewMode;
  const nextState = {
    ...state,
    agentMode,
    agentModePickerOpen: false,
    aiSessionTitleGenerationEnabled,
    authAccessToken: authState.authAccessToken,
    authEnabled,
    authErrorMessage: authState.authErrorMessage,
    authUseRedirect,
    backendUrl,
    chatHistoryEnabled,
    chatListScrollTop,
    composerAttachments,
    composerAttachmentsHeight: getComposerAttachmentsHeight(composerAttachments, state.width),
    composerDropActive: false,
    composerDropEnabled,
    composerSelectionEnd,
    composerSelectionStart,
    composerValue,
    emitStreamingFunctionCallEvents,
    initial: false,
    lastNormalViewMode,
    messages,
    messagesScrollTop,
    modelPickerHeight: getModelPickerHeight(state.modelPickerHeaderHeight, visibleModels.length),
    modelPickerListScrollTop: 0,
    modelPickerOpen: false,
    modelPickerSearchValue: '',
    openApiApiKey,
    openApiApiKeyInput: openApiApiKey,
    openRouterApiKey,
    openRouterApiKeyInput: openRouterApiKey,
    parsedMessages,
    passIncludeObfuscation,
    projectExpandedIds,
    projectListScrollTop,
    projects,
    projectSidebarResizing: false,
    projectSidebarWidth,
    reasoningEffort,
    reasoningEffortPickerOpen: false,
    reasoningPickerEnabled,
    runModePickerOpen: false,
    scrollDownButtonEnabled,
    searchEnabled,
    searchFieldVisible: false,
    searchValue: '',
    selectedModelId,
    selectedProjectId,
    selectedSessionId,
    sessions,
    showChatListTime,
    showModelUsageMultiplier,
    showRunMode: runModePickerEnabled,
    streamingEnabled,
    todoListToolEnabled,
    toolEnablement,
    useAuthWorker,
    useChatCoordinatorWorker,
    useChatMathWorker,
    useChatNetworkWorkerForRequests,
    useChatToolWorker,
    useOwnBackend,
    userName: authState.userName,
    userState: authState.userState,
    userSubscriptionPlan: authState.userSubscriptionPlan,
    userUsedTokens: authState.userUsedTokens,
    viewMode,
    visibleModels,
    voiceDictationEnabled
  };
  const refreshedState = await refreshGitBranchPickerVisibility(nextState);
  if (selectedSessionId) {
    await subscribeSessionUpdates(state.uid, selectedSessionId);
    setSubscribedSessionId(state.uid, selectedSessionId);
  }
  setState(state.uid, refreshedState);
  return refreshedState;
};

const isObject = value => {
  return !!value && typeof value === 'object';
};
const getDelay = payload => {
  if (!isObject(payload)) {
    return 0;
  }
  const {
    delay
  } = payload;
  return typeof delay === 'number' && delay > 0 ? delay : 0;
};
const mockBackendAuthResponse = (state, payload) => {
  const delay = getDelay(payload);
  if (payload.type === 'error') {
    setNextRefreshResponse({
      delay,
      ...(payload.errorName && {
        errorName: payload.errorName
      }),
      message: payload.message || 'Backend authentication failed.',
      type: 'error'
    });
    return state;
  }
  setNextRefreshResponse({
    delay,
    response: payload,
    type: 'success'
  });
  return state;
};

const getSessionStatus = messages => {
  return messages.some(message => message.role === 'assistant') ? 'finished' : 'idle';
};
const applySessionOptions = (session, options) => {
  if (!options) {
    return session;
  }
  return {
    ...session,
    ...(options.branchName && {
      branchName: options.branchName
    }),
    ...(options.lastActiveTime && {
      lastActiveTime: options.lastActiveTime
    }),
    ...(options.projectId && {
      projectId: options.projectId
    }),
    ...(options.workspaceUri && {
      workspaceUri: options.workspaceUri
    })
  };
};
const openMockSession = async (state, mockSessionId, mockChatMessages, options) => {
  const {
    sessions: currentSessions
  } = state;
  if (!mockSessionId) {
    return state;
  }
  const parsedMessages = await parseAndStoreMessagesContent(state.parsedMessages, mockChatMessages);
  const existingSession = currentSessions.find(session => session.id === mockSessionId);
  const sessions = existingSession ? currentSessions.map(session => {
    if (session.id !== mockSessionId) {
      return session;
    }
    return applySessionOptions({
      ...session,
      messages: [],
      status: getSessionStatus(mockChatMessages)
    }, options);
  }) : [...currentSessions, applySessionOptions({
    id: mockSessionId,
    messages: [],
    status: getSessionStatus(mockChatMessages),
    title: mockSessionId
  }, options)];
  const selectedSession = sessions.find(session => session.id === mockSessionId);
  if (selectedSession) {
    await saveChatSession({
      ...selectedSession,
      messages: mockChatMessages
    });
  }
  const nextState = await refreshGitBranchPickerVisibility({
    ...state,
    composerAttachments: [],
    composerAttachmentsHeight: 0,
    messages: mockChatMessages,
    parsedMessages,
    renamingSessionId: '',
    selectedSessionId: mockSessionId,
    sessions,
    viewMode: 'detail'
  });
  setState(state.uid, nextState);
  return nextState;
};

const commandMap = {
  'ChatModel.appendChatViewEvent': appendChatViewEvent,
  'ChatModel.clearChatSessions': clearChatSessions,
  'ChatModel.deleteChatSession': deleteChatSession,
  'ChatModel.getChatSession': getChatSession,
  'ChatModel.getChatViewEvents': getChatViewEvents,
  'ChatModel.getState': getState,
  'ChatModel.handleClickBack': handleClickBack,
  'ChatModel.handleInput': handleInput,
  'ChatModel.handleSubmit': handleRpcSubmit,
  'ChatModel.listChatSessions': listChatSessions,
  'ChatModel.loadContent': loadContent,
  'ChatModel.mockBackendAuthResponse': mockBackendAuthResponse,
  'ChatModel.openMockSession': openMockSession,
  'ChatModel.resetChatSessionStorage': resetChatSessionStorage,
  'ChatModel.saveChatSession': saveChatSession,
  handleChatStorageUpdate,
  initialize: (_, port) => handleMessagePort(port),
  'ViewModel.handleMessagePort': handleMessagePort
};

const sendMessagePortToAuthWorker = async port => {
  await sendMessagePortToAuthWorker$1(port, 0);
};
const initializeAuthWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToAuthWorker
  });
  set$b(rpc);
};

const sendMessagePortToChatCoordinatorWorker = async port => {
  await sendMessagePortToChatCoordinatorWorker$1(port, 0);
};
const initializeChatCoordinatorWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToChatCoordinatorWorker
  });
  set$a(rpc);
};

const sendMessagePortToChatMathWorker = async port => {
  await sendMessagePortToChatMathWorker$1(port, 0);
};
const initializeChatMathWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToChatMathWorker
  });
  set$9(rpc);
};

const send$2 = port => {
  return sendMessagePortToChatMessageParsingWorker(port, 0);
};
const initializeChatMessageParsingWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: send$2
  });
  set$8(rpc);
};

const send$1 = port => {
  return sendMessagePortToChatNetworkWorker(port);
};
const initializeChatNetworkWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: send$1
  });
  set$7(rpc);
};

const sendMessagePortToChatStorageWorker = async port => {
  // @ts-ignore
  await sendMessagePortToChatStorageWorker$1(port, rpcIdViewModel);
};
const initializeChatStorageWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToChatStorageWorker
  });
  set$6(rpc);
};

const send = port => {
  return sendMessagePortToChatToolWorker(port);
};
const initializeChatToolWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send
  });
  set$5(rpc);
};

const sendMessagePortToClipBoardWorker = async port => {
  await sendMessagePortToClipBoardWorker$1(port, 0);
};
const initializeClipBoardWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToClipBoardWorker
  });
  set$4(rpc);
};

const sendMessagePortToIconThemeWorker = async port => {
  await sendMessagePortToIconThemeWorker$1(port, 0);
};
const initializeIconThemeWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToIconThemeWorker
  });
  set$3(rpc);
};

const sendMessagePortToOpenerWorker = async port => {
  await sendMessagePortToOpenerWorker$1(port, 0);
};
const initializeOpenerWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToOpenerWorker
  });
  set$2(rpc);
};

const sendMessagePortToTextMeasurementWorker = async port => {
  await sendMessagePortToTextMeasurementWorker$1(port);
};
const initializeTextMeasurementWorker = async () => {
  const rpc = await create$2({
    commandMap: {},
    send: sendMessagePortToTextMeasurementWorker
  });
  set(rpc);
};

const listen = async () => {
  const rpc = await create$1({
    commandMap: commandMap
  });
  set$1(rpc);
  await Promise.all([initializeAuthWorker(), initializeChatCoordinatorWorker(), initializeChatMathWorker(), initializeChatMessageParsingWorker(), initializeChatNetworkWorker(), initializeChatStorageWorker(), initializeChatToolWorker(), initializeClipBoardWorker(), initializeIconThemeWorker(), initializeOpenerWorker(), initializeTextMeasurementWorker()]);
};

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

main();
