Index: frontend/node_modules/launch-editor/LICENSE
===================================================================
--- frontend/node_modules/launch-editor/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017-present, Yuxi (Evan) You
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
Index: frontend/node_modules/launch-editor/editor-info/linux.js
===================================================================
--- frontend/node_modules/launch-editor/editor-info/linux.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/editor-info/linux.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,30 @@
+module.exports = {
+  atom: 'atom',
+  Brackets: 'brackets',
+  'code-insiders': 'code-insiders',
+  code: 'code',
+  vscodium: 'vscodium',
+  codium: 'codium',
+  cursor: 'cursor',
+  trae: 'trae',
+  antigravity: 'antigravity',
+  emacs: 'emacs',
+  gvim: 'gvim',
+  idea: 'idea',
+  'idea.sh': 'idea',
+  phpstorm: 'phpstorm',
+  'phpstorm.sh': 'phpstorm',
+  pycharm: 'pycharm',
+  'pycharm.sh': 'pycharm',
+  rubymine: 'rubymine',
+  'rubymine.sh': 'rubymine',
+  sublime_text: 'subl',
+  vim: 'vim',
+  webstorm: 'webstorm',
+  'webstorm.sh': 'webstorm',
+  goland: 'goland',
+  'goland.sh': 'goland',
+  rider: 'rider',
+  'rider.sh': 'rider',
+  zed: 'zed'
+}
Index: frontend/node_modules/launch-editor/editor-info/macos.js
===================================================================
--- frontend/node_modules/launch-editor/editor-info/macos.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/editor-info/macos.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,50 @@
+module.exports = {
+  '/Applications/Atom.app/Contents/MacOS/Atom': 'atom',
+  '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta':
+    '/Applications/Atom Beta.app/Contents/MacOS/Atom Beta',
+  '/Applications/Brackets.app/Contents/MacOS/Brackets': 'brackets',
+  '/Applications/Sublime Text.app/Contents/MacOS/Sublime Text':
+    '/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl',
+  '/Applications/Sublime Text.app/Contents/MacOS/sublime_text':
+    '/Applications/Sublime Text.app/Contents/SharedSupport/bin/subl',
+  '/Applications/Sublime Text 2.app/Contents/MacOS/Sublime Text 2':
+    '/Applications/Sublime Text 2.app/Contents/SharedSupport/bin/subl',
+  '/Applications/Sublime Text Dev.app/Contents/MacOS/Sublime Text':
+    '/Applications/Sublime Text Dev.app/Contents/SharedSupport/bin/subl',
+  '/Applications/Visual Studio Code.app/Contents/MacOS/Code': 'code',
+  '/Applications/Visual Studio Code.app/Contents/MacOS/Electron': 'code',
+  '/Applications/Visual Studio Code - Insiders.app/Contents/MacOS/Code - Insiders':
+    'code-insiders',
+  '/Applications/Visual Studio Code - Insiders.app/Contents/MacOS/Electron':
+    'code-insiders',
+  '/Applications/VSCodium.app/Contents/MacOS/Electron': 'codium',
+  '/Applications/Cursor.app/Contents/MacOS/Cursor': 'cursor',
+  '/Applications/Trae.app/Contents/MacOS/Electron': 'trae',
+  '/Applications/Antigravity.app/Contents/MacOS/Electron': 'antigravity',
+  '/Applications/AppCode.app/Contents/MacOS/appcode':
+    '/Applications/AppCode.app/Contents/MacOS/appcode',
+  '/Applications/CLion.app/Contents/MacOS/clion':
+    '/Applications/CLion.app/Contents/MacOS/clion',
+  '/Applications/IntelliJ IDEA.app/Contents/MacOS/idea':
+    '/Applications/IntelliJ IDEA.app/Contents/MacOS/idea',
+  '/Applications/IntelliJ IDEA Ultimate.app/Contents/MacOS/idea':
+    '/Applications/IntelliJ IDEA Ultimate.app/Contents/MacOS/idea',
+  '/Applications/IntelliJ IDEA Community Edition.app/Contents/MacOS/idea':
+    '/Applications/IntelliJ IDEA Community Edition.app/Contents/MacOS/idea',
+  '/Applications/PhpStorm.app/Contents/MacOS/phpstorm':
+    '/Applications/PhpStorm.app/Contents/MacOS/phpstorm',
+  '/Applications/PyCharm.app/Contents/MacOS/pycharm':
+    '/Applications/PyCharm.app/Contents/MacOS/pycharm',
+  '/Applications/PyCharm CE.app/Contents/MacOS/pycharm':
+    '/Applications/PyCharm CE.app/Contents/MacOS/pycharm',
+  '/Applications/RubyMine.app/Contents/MacOS/rubymine':
+    '/Applications/RubyMine.app/Contents/MacOS/rubymine',
+  '/Applications/WebStorm.app/Contents/MacOS/webstorm':
+    '/Applications/WebStorm.app/Contents/MacOS/webstorm',
+  '/Applications/MacVim.app/Contents/MacOS/MacVim': 'mvim',
+  '/Applications/GoLand.app/Contents/MacOS/goland':
+    '/Applications/GoLand.app/Contents/MacOS/goland',
+  '/Applications/Rider.app/Contents/MacOS/rider':
+    '/Applications/Rider.app/Contents/MacOS/rider',
+  '/Applications/Zed.app/Contents/MacOS/zed': 'zed'
+}
Index: frontend/node_modules/launch-editor/editor-info/windows.js
===================================================================
--- frontend/node_modules/launch-editor/editor-info/windows.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/editor-info/windows.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,29 @@
+module.exports = [
+  'Brackets.exe',
+  'Code.exe',
+  'Code - Insiders.exe',
+  'VSCodium.exe',
+  'Cursor.exe',
+  'atom.exe',
+  'sublime_text.exe',
+  'notepad++.exe',
+  'clion.exe',
+  'clion64.exe',
+  'idea.exe',
+  'idea64.exe',
+  'phpstorm.exe',
+  'phpstorm64.exe',
+  'pycharm.exe',
+  'pycharm64.exe',
+  'rubymine.exe',
+  'rubymine64.exe',
+  'webstorm.exe',
+  'webstorm64.exe',
+  'goland.exe',
+  'goland64.exe',
+  'rider.exe',
+  'rider64.exe',
+  'Trae.exe',
+  'zed.exe',
+  'Antigravity.exe'
+]
Index: frontend/node_modules/launch-editor/get-args.js
===================================================================
--- frontend/node_modules/launch-editor/get-args.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/get-args.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,76 @@
+const path = require('path')
+
+// normalize file/line numbers into command line args for specific editors
+module.exports = function getArgumentsForPosition (
+  editor,
+  fileName,
+  lineNumber,
+  columnNumber = 1
+) {
+  const editorBasename = path.basename(editor).replace(/\.(exe|cmd|bat)$/i, '')
+  switch (editorBasename) {
+    case 'atom':
+    case 'Atom':
+    case 'Atom Beta':
+    case 'subl':
+    case 'sublime':
+    case 'sublime_text':
+    case 'wstorm':
+    case 'charm':
+    case 'zed':
+      return [`${fileName}:${lineNumber}:${columnNumber}`]
+    case 'notepad++':
+      return ['-n' + lineNumber, '-c' + columnNumber, fileName]
+    case 'vim':
+    case 'mvim':
+      return [`+call cursor(${lineNumber}, ${columnNumber})`, fileName]
+    case 'joe':
+    case 'gvim':
+      return ['+' + `${lineNumber}`, fileName]
+    case 'emacs':
+    case 'emacsclient':
+      return [`+${lineNumber}:${columnNumber}`, fileName]
+    case 'rmate':
+    case 'mate':
+    case 'mine':
+      return ['--line', lineNumber, fileName]
+    case 'code':
+    case 'Code':
+    case 'code-insiders':
+    case 'Code - Insiders':
+    case 'codium':
+    case 'trae':
+    case 'antigravity':
+    case 'cursor':
+    case 'vscodium':
+    case 'VSCodium':
+      return ['-r', '-g', `${fileName}:${lineNumber}:${columnNumber}`]
+    case 'appcode':
+    case 'clion':
+    case 'clion64':
+    case 'idea':
+    case 'idea64':
+    case 'phpstorm':
+    case 'phpstorm64':
+    case 'pycharm':
+    case 'pycharm64':
+    case 'rubymine':
+    case 'rubymine64':
+    case 'webstorm':
+    case 'webstorm64':
+    case 'goland':
+    case 'goland64':
+    case 'rider':
+    case 'rider64':
+      return ['--line', lineNumber, '--column', columnNumber, fileName]
+  }
+
+  if (process.env.LAUNCH_EDITOR) {
+    return [fileName, lineNumber, columnNumber]
+  }
+
+  // For all others, drop the lineNumber until we have
+  // a mapping above, since providing the lineNumber incorrectly
+  // can result in errors or confusing behavior.
+  return [fileName]
+}
Index: frontend/node_modules/launch-editor/guess.js
===================================================================
--- frontend/node_modules/launch-editor/guess.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/guess.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,108 @@
+const path = require('path')
+const shellQuote = require('shell-quote')
+const childProcess = require('child_process')
+
+// Map from full process name to binary that starts the process
+// We can't just re-use full process name, because it will spawn a new instance
+// of the app every time
+const COMMON_EDITORS_MACOS = require('./editor-info/macos')
+const COMMON_EDITORS_LINUX = require('./editor-info/linux')
+const COMMON_EDITORS_WIN = require('./editor-info/windows')
+
+module.exports = function guessEditor (specifiedEditor) {
+  if (specifiedEditor) {
+    return shellQuote.parse(specifiedEditor)
+  }
+
+  if (process.env.LAUNCH_EDITOR) {
+    return [process.env.LAUNCH_EDITOR]
+  }
+
+  if (process.versions.webcontainer) {
+    return [process.env.EDITOR || 'code']
+  }
+
+  // We can find out which editor is currently running by:
+  // `ps x` on macOS and Linux
+  // `Get-Process` on Windows
+  try {
+    if (process.platform === 'darwin') {
+      const output = childProcess
+        .execSync('ps x -o comm=', {
+          stdio: ['pipe', 'pipe', 'ignore']
+        })
+        .toString()
+      const processNames = Object.keys(COMMON_EDITORS_MACOS)
+      const processList = output.split('\n')
+      for (let i = 0; i < processNames.length; i++) {
+        const processName = processNames[i]
+        // Find editor by exact match.
+        if (processList.includes(processName)) {
+          return [COMMON_EDITORS_MACOS[processName]]
+        }
+        const processNameWithoutApplications = processName.replace('/Applications', '')
+        // Find editor installation not in /Applications.
+        if (output.indexOf(processNameWithoutApplications) !== -1) {
+          // Use the CLI command if one is specified
+          if (processName !== COMMON_EDITORS_MACOS[processName]) {
+            return [COMMON_EDITORS_MACOS[processName]]
+          }
+          // Use a partial match to find the running process path.  If one is found, use the
+          // existing path since it can be running from anywhere.
+          const runningProcess = processList.find((procName) => procName.endsWith(processNameWithoutApplications))
+          if (runningProcess !== undefined) {
+            return [runningProcess]
+          }
+        }
+      }
+    } else if (process.platform === 'win32') {
+      const output = childProcess
+        .execSync(
+          'powershell -NoProfile -Command "' +
+            '[Console]::OutputEncoding=[Text.Encoding]::UTF8;' +
+            'Get-CimInstance -Query \\"select executablepath from win32_process where executablepath is not null\\" | % { $_.ExecutablePath }' +
+            '"',
+          {
+            stdio: ['pipe', 'pipe', 'ignore']
+          }
+        )
+        .toString()
+      const runningProcesses = output.split('\r\n')
+      for (let i = 0; i < runningProcesses.length; i++) {
+        const fullProcessPath = runningProcesses[i].trim()
+        const shortProcessName = path.basename(fullProcessPath)
+
+        if (COMMON_EDITORS_WIN.indexOf(shortProcessName) !== -1) {
+          return [fullProcessPath]
+        }
+      }
+    } else if (process.platform === 'linux') {
+      // --no-heading No header line
+      // x List all processes owned by you
+      // -o comm Need only names column
+      const output = childProcess
+        .execSync('ps x --no-heading -o comm --sort=comm', {
+          stdio: ['pipe', 'pipe', 'ignore']
+        })
+        .toString()
+      const processNames = Object.keys(COMMON_EDITORS_LINUX)
+      for (let i = 0; i < processNames.length; i++) {
+        const processName = processNames[i]
+        if (output.indexOf(processName) !== -1) {
+          return [COMMON_EDITORS_LINUX[processName]]
+        }
+      }
+    }
+  } catch (ignoreError) {
+    // Ignore...
+  }
+
+  // Last resort, use old skool env vars
+  if (process.env.VISUAL) {
+    return [process.env.VISUAL]
+  } else if (process.env.EDITOR) {
+    return [process.env.EDITOR]
+  }
+
+  return [null]
+}
Index: frontend/node_modules/launch-editor/index.d.ts
===================================================================
--- frontend/node_modules/launch-editor/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,16 @@
+/**
+ * Launch an editor to open a file at a specific line and column.
+ *
+ * @param file File path with optional line and column numbers (e.g.
+ *   "/path/to/file.js:10:2").
+ * @param specifiedEditor Optional editor command or path to use. Will be
+ *   parsed using `shell-quote`.
+ * @param onErrorCallback Optional callback for handling errors.
+ */
+declare function launchEditor(
+  file: string,
+  specifiedEditor?: string | ((fileName: string, errorMessage: string | null) => void),
+  onErrorCallback?: (fileName: string, errorMessage: string | null) => void
+): void;
+
+export = launchEditor;
Index: frontend/node_modules/launch-editor/index.js
===================================================================
--- frontend/node_modules/launch-editor/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2015-present, Facebook, Inc.
+ *
+ * This source code is licensed under the MIT license found in the
+ * LICENSE file at
+ * https://github.com/facebookincubator/create-react-app/blob/master/LICENSE
+ *
+ * Modified by Yuxi Evan You
+ */
+
+const fs = require('fs')
+const os = require('os')
+const path = require('path')
+const colors = require('picocolors')
+const childProcess = require('child_process')
+
+const guessEditor = require('./guess')
+const getArgumentsForPosition = require('./get-args')
+
+function wrapErrorCallback (cb) {
+  return (fileName, errorMessage) => {
+    console.log()
+    console.log(
+      colors.red('Could not open ' + path.basename(fileName) + ' in the editor.')
+    )
+    if (errorMessage) {
+      if (errorMessage[errorMessage.length - 1] !== '.') {
+        errorMessage += '.'
+      }
+      console.log(
+        colors.red('The editor process exited with an error: ' + errorMessage)
+      )
+    }
+    console.log()
+    if (cb) cb(fileName, errorMessage)
+  }
+}
+
+function isTerminalEditor (editor) {
+  switch (editor) {
+    case 'vim':
+    case 'emacs':
+    case 'nano':
+      return true
+  }
+  return false
+}
+
+const positionRE = /:(\d+)(:(\d+))?$/
+function parseFile (file) {
+  // support `file://` protocol
+  if (file.startsWith('file://')) {
+    file = require('url').fileURLToPath(file)
+  }
+
+  const fileName = file.replace(positionRE, '')
+  const match = file.match(positionRE)
+  const lineNumber = match && match[1]
+  const columnNumber = match && match[3]
+  return {
+    fileName,
+    lineNumber,
+    columnNumber
+  }
+}
+
+let _childProcess = null
+
+function launchEditor (file, specifiedEditor, onErrorCallback) {
+  const parsed = parseFile(file)
+  let { fileName } = parsed
+  const { lineNumber, columnNumber } = parsed
+
+  if (!fs.existsSync(fileName)) {
+    return
+  }
+
+  if (typeof specifiedEditor === 'function') {
+    onErrorCallback = specifiedEditor
+    specifiedEditor = undefined
+  }
+
+  onErrorCallback = wrapErrorCallback(onErrorCallback)
+
+  const [editor, ...args] = guessEditor(specifiedEditor)
+  if (!editor) {
+    onErrorCallback(fileName, null)
+    return
+  }
+
+  if (
+    process.platform === 'linux' &&
+    fileName.startsWith('/mnt/') &&
+    /Microsoft/i.test(os.release())
+  ) {
+    // Assume WSL / "Bash on Ubuntu on Windows" is being used, and
+    // that the file exists on the Windows file system.
+    // `os.release()` is "4.4.0-43-Microsoft" in the current release
+    // build of WSL, see: https://github.com/Microsoft/BashOnWindows/issues/423#issuecomment-221627364
+    // When a Windows editor is specified, interop functionality can
+    // handle the path translation, but only if a relative path is used.
+    fileName = path.relative('', fileName)
+  }
+
+  if (lineNumber) {
+    const extraArgs = getArgumentsForPosition(editor, fileName, lineNumber, columnNumber)
+    args.push.apply(args, extraArgs)
+  } else {
+    args.push(fileName)
+  }
+
+  if (_childProcess && isTerminalEditor(editor)) {
+    // There's an existing editor process already and it's attached
+    // to the terminal, so go kill it. Otherwise two separate editor
+    // instances attach to the stdin/stdout which gets confusing.
+    _childProcess.kill('SIGKILL')
+  }
+
+  if (process.platform === 'win32') {
+    // On Windows, we need to use `exec` with the `shell: true` option,
+    // and some more sanitization is required.
+
+    // However, CMD.exe on Windows is vulnerable to RCE attacks given a file name of the
+    // form "C:\Users\myusername\Downloads\& curl 172.21.93.52".
+    // `create-react-app` used a safe file name pattern to validate user-provided file names:
+    // - https://github.com/facebook/create-react-app/pull/4866
+    // - https://github.com/facebook/create-react-app/pull/5431
+    // But that's not a viable solution for this package because
+    // it's depended on by so many meta frameworks that heavily rely on
+    // special characters in file names for filesystem-based routing.
+    // We need to at least:
+    // - Support `+` because it's used in SvelteKit and Vike
+    // - Support `$` because it's used in Remix
+    // - Support `(` and `)` because they are used in Analog, SolidStart, and Vike
+    // - Support `@` because it's used in Vike
+    // - Support `[` and `]` because they are widely used for [slug]
+    // So here we choose to use `^` to escape special characters instead.
+
+    // According to https://ss64.com/nt/syntax-esc.html,
+    // we can use `^` to escape `&`, `<`, `>`, `|`, `%`, and `^`
+    // I'm not sure if we have to escape all of these, but let's do it anyway
+    function escapeCmdArgs (cmdArgs) {
+      return cmdArgs.replace(/([&|<>,;=^])/g, '^$1')
+    }
+
+    // Need to double quote the editor path in case it contains spaces;
+    // If the fileName contains spaces, we also need to double quote it in the arguments
+    // However, there's a case that it's concatenated with line number and column number
+    // which is separated by `:`. We need to double quote the whole string in this case.
+    // Also, if the string contains the escape character `^`, it needs to be quoted, too.
+    function doubleQuoteIfNeeded(str) {
+      if (str.includes('^')) {
+        // If a string includes an escaped character, not only does it need to be quoted,
+        // but the quotes need to be escaped too.
+        return `^"${str}^"`
+      } else if (str.includes(' ')) {
+        return `"${str}"`
+      } 
+      return str
+    }
+    const launchCommand = [editor, ...args.map(escapeCmdArgs)]
+      .map(doubleQuoteIfNeeded)
+      .join(' ')
+
+    _childProcess = childProcess.exec(launchCommand, {
+      stdio: 'inherit',
+      shell: true
+    })
+  } else {
+    _childProcess = childProcess.spawn(editor, args, { stdio: 'inherit' })
+  }
+  _childProcess.on('exit', function (errorCode) {
+    _childProcess = null
+
+    if (errorCode) {
+      onErrorCallback(fileName, '(code ' + errorCode + ')')
+    }
+  })
+
+  _childProcess.on('error', function (error) {
+    let { code, message } = error
+    if ('ENOENT' === code) {
+      message = `${message} ('${editor}' command does not exist in 'PATH')`
+    }
+    onErrorCallback(fileName, message);
+  })
+}
+
+module.exports = launchEditor
Index: frontend/node_modules/launch-editor/package.json
===================================================================
--- frontend/node_modules/launch-editor/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/launch-editor/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,24 @@
+{
+  "name": "launch-editor",
+  "version": "2.13.2",
+  "description": "launch editor from node.js",
+  "main": "index.js",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/yyx990803/launch-editor.git"
+  },
+  "keywords": [
+    "launch",
+    "editor"
+  ],
+  "author": "Evan You",
+  "license": "MIT",
+  "bugs": {
+    "url": "https://github.com/yyx990803/launch-editor/issues"
+  },
+  "homepage": "https://github.com/yyx990803/launch-editor#readme",
+  "dependencies": {
+    "picocolors": "^1.1.1",
+    "shell-quote": "^1.8.3"
+  }
+}
