UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

728 lines (727 loc) 29.5 kB
"use strict"; /** * Electron Main Process Entry Point * * ARCHITECTURE: * This is the main entry point for the Minecraft Creator Tools Electron app. * It orchestrates: * - App lifecycle (startup, squirrel events, single instance) * - Window creation and management * - IPC handler registration for renderer communication * - Command handlers for server, file system features * * COMMAND LINE ARGUMENTS: * - -h, --help: Show help * - -i <path>: Input path to open * - -o, --out <path>: Output path for exports * - -c, --cmd <command>: Run a specific command (exportworld, help) * - --storage <path>: Custom storage path for all app data * * APP MODES: * - APP_MODE_MAINAPP (0): Normal Minecraft Creator Tools * - APP_MODE_EXPORTWORLD (1): Export world command * - APP_MODE_ERRORHELP (99): Show help and exit * - APP_MODE_EXIT (100): Exit immediately */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const path = __importStar(require("path")); const open_1 = __importDefault(require("open")); const electron_1 = require("electron"); // @ts-ignore - electron-squirrel-startup doesn't have type declarations const electron_squirrel_startup_1 = __importDefault(require("electron-squirrel-startup")); const ChildProcess = __importStar(require("child_process")); const fs = __importStar(require("fs")); const CreatorToolsHost_1 = __importDefault(require("../app/CreatorToolsHost")); const Database_1 = __importDefault(require("../minecraft/Database")); const NodeStorage_1 = __importDefault(require("../local/NodeStorage")); const WindowCommandHandler_1 = __importDefault(require("./WindowCommandHandler")); const DedicatedServerCommandHandler_1 = __importDefault(require("./DedicatedServerCommandHandler")); const WebSocketCommandHandler_1 = __importDefault(require("./WebSocketCommandHandler")); const FileSystemCommandHandler_1 = __importDefault(require("./FileSystemCommandHandler")); const ContentSourceManager_1 = __importDefault(require("./ContentSourceManager")); const LocalCommandHandler_1 = __importDefault(require("./LocalCommandHandler")); const ElectronUtils_1 = __importDefault(require("./ElectronUtils")); const ContentLogWatcher_1 = __importDefault(require("../local/ContentLogWatcher")); const StorageUtilities_1 = __importDefault(require("../storage/StorageUtilities")); const LocalEnvironment_1 = __importDefault(require("../local/LocalEnvironment")); const Log_1 = __importDefault(require("../core/Log")); // Error handler for uncaught exceptions. // Before exiting, attempt to forward the error to the focused renderer so it // can display the global error overlay (giving the user a chance to copy // details). We delay the exit briefly so the IPC message has time to land. function handleException(err) { Log_1.default.error("Unfortunately, the application could not continue to run.\n\nApplication error:\n" + (err.message ? err.message : err.toString()) + "\n\nReturning to prompt.\n"); process.removeListener("uncaughtException", handleException); let notifiedRenderer = false; try { const target = electron_1.BrowserWindow.getFocusedWindow() || (electron_1.BrowserWindow.getAllWindows().length > 0 ? electron_1.BrowserWindow.getAllWindows()[0] : undefined); if (target && !target.isDestroyed()) { target.webContents.send("error:fatal", { message: err?.message || String(err), stack: err?.stack, }); notifiedRenderer = true; } } catch { // ignore — best effort only } // Give the renderer a moment to paint the overlay before tearing down. setTimeout(() => electron_1.app.exit(9), notifiedRenderer ? 2000 : 0); } // Use production mode if packaged, or if ELECTRON_FORCE_PROD is set (for testing with built files) const isDev = !electron_1.app.isPackaged && process.env.ELECTRON_FORCE_PROD !== "true"; // Support custom dev URL override (e.g., ELECTRON_DEV_URL=http://localhost:3001) const devUrl = process.env.ELECTRON_DEV_URL || "http://localhost:3000"; Log_1.default.debug(`[Electron] Mode: ${isDev ? "DEVELOPMENT" : "PRODUCTION"}`); Log_1.default.debug(`[Electron] Loading from: ${isDev ? devUrl : "build/ assets"}`); CreatorToolsHost_1.default.setHostType(2); // Install extensions in dev mode let installExtension; let REACT_DEVELOPER_TOOLS; if (isDev) { const devTools = require("electron-devtools-installer"); installExtension = devTools.default; REACT_DEVELOPER_TOOLS = devTools.REACT_DEVELOPER_TOOLS; } else { process.on("uncaughtException", handleException); } // Squirrel setup handling const appFolder = path.resolve(process.execPath, ".."); const rootAtomFolder = path.resolve(appFolder, ".."); const updateDotExe = path.resolve(path.join(rootAtomFolder, "Update.exe")); const exeName = path.basename(process.execPath); function spawn(command, args) { let spawnedProcess = undefined; try { spawnedProcess = ChildProcess.spawn(command, args, { detached: true }); } catch (error) { Log_1.default.error("Error starting process: " + error); } return spawnedProcess; } function spawnUpdate(args) { return spawn(updateDotExe, args); } function handleStartupEvent() { if (process.platform !== "win32") { return false; } const squirrelCommand = process.argv[1]; switch (squirrelCommand) { case "--squirrel-install": case "--squirrel-updated": spawnUpdate(["--createShortcut", exeName]); setTimeout(electron_1.app.quit, 1000); return true; case "--squirrel-uninstall": spawnUpdate(["--removeShortcut", exeName]); setTimeout(electron_1.app.quit, 1000); return true; case "--squirrel-obsolete": electron_1.app.quit(); return true; default: return false; } } handleStartupEvent(); if (electron_squirrel_startup_1.default) { electron_1.app.quit(); } // App state let _mainWindow = null; let _commandHandler = null; let _dedicatedServerCommandHandler = null; let _webSocketCommandHandler = null; let _fileSystemCommandHandler = null; let _contentSourceManager = null; let _localCommandHandler = null; let _tray = null; let _appIsReady = false; let _creatorToolsIsReady = false; let _inputPath = null; let _outPath = null; let _oper = null; let _appFolderNameLong = "Minecraft Creator Tools"; let _appFolderNameShort = "mctools"; let _contentLogger = undefined; let _creatorTools = undefined; let _env = undefined; let _utils = undefined; let _customStoragePath = undefined; const APP_MODE_MAINAPP = 0; const APP_MODE_EXPORTWORLD = 1; const APP_MODE_ERRORHELP = 99; const APP_MODE_EXIT = 100; let _mode = APP_MODE_MAINAPP; const _appLock = electron_1.app.requestSingleInstanceLock({ mode: _mode, initTime: new Date().getTime() }); parseCommandLineArgs(); /** * Get the base storage path for the app. */ function _getStorageBasePath() { if (_customStoragePath) { let storagePath = _customStoragePath; if (!storagePath.endsWith(NodeStorage_1.default.slashFolderDelimiter)) { storagePath += NodeStorage_1.default.slashFolderDelimiter; } Log_1.default.debug("[Electron] Using custom storage path from --storage:", storagePath); return storagePath; } const testStorageRoot = process.env.MCT_TEST_STORAGE_ROOT; if (testStorageRoot) { let storagePath = testStorageRoot; if (!storagePath.endsWith(NodeStorage_1.default.slashFolderDelimiter)) { storagePath += NodeStorage_1.default.slashFolderDelimiter; } Log_1.default.debug("[Electron] Using test storage from MCT_TEST_STORAGE_ROOT:", storagePath); return storagePath; } return (electron_1.app.getPath("documents") + NodeStorage_1.default.slashFolderDelimiter + _appFolderNameLong + NodeStorage_1.default.slashFolderDelimiter); } function _getCreatorTools() { const basePath = _getStorageBasePath(); Log_1.default.debug("[Electron] Storage base path:", basePath); CreatorToolsHost_1.default.prefsStorage = new NodeStorage_1.default(basePath + "prefs" + NodeStorage_1.default.slashFolderDelimiter, ""); CreatorToolsHost_1.default.projectsStorage = new NodeStorage_1.default(basePath + "projects" + NodeStorage_1.default.slashFolderDelimiter, ""); CreatorToolsHost_1.default.packStorage = new NodeStorage_1.default(basePath + "packs" + NodeStorage_1.default.slashFolderDelimiter, ""); CreatorToolsHost_1.default.worldStorage = new NodeStorage_1.default(basePath + "worlds" + NodeStorage_1.default.slashFolderDelimiter, ""); CreatorToolsHost_1.default.init(); const ct = CreatorToolsHost_1.default.getCreatorTools(); if (ct) { ct.local = _env.utilities; } return ct; } async function _load() { _env = new LocalEnvironment_1.default(true); _env.setWorldContainerPath(_getStorageBasePath() + "worlds" + path.sep); _env.utilities.setProductNameSeed(_appFolderNameShort); await _env.load(); _utils = new ElectronUtils_1.default(_env); _creatorTools = _getCreatorTools(); await _creatorTools.load(); _contentLogger = new ContentLogWatcher_1.default(_env); Database_1.default.local = _env.utilities; _creatorToolsIsReady = true; _creatorTools.onStatusAdded.subscribe(_handleStatusAdded); _startWindow(); } _load(); if (!_appLock) { Log_1.default.debug("App is already running; exiting this instance."); electron_1.app.quit(); _mode = APP_MODE_EXIT; } else { electron_1.app.on("second-instance", (_event, _commandLine, _workingDirectory, _additionalData) => { if (_mainWindow) { if (_mainWindow.isMinimized()) { _mainWindow.restore(); } _mainWindow.focus(); } }); } function _handleStatusAdded(_carto, statusItem) { Log_1.default.debug("Server Manager: " + statusItem.message); if (_mainWindow !== null && _mainWindow !== undefined) { _mainWindow.webContents.send("appsvc", "statusMessage|" + JSON.stringify(statusItem)); } } function parseCommandLineArgs() { // In Electron, process.argv looks like: // [0] = path to electron executable // [1] = path to main script (e.g., main.mjs) // [2+] = actual user arguments // We need to skip argv[1] when checking for positional input path const argsStartIndex = electron_1.app.isPackaged ? 1 : 2; // Packaged app doesn't have main script in argv for (let i = 1; i < process.argv.length; i++) { const val = process.argv[i].toLowerCase(); if (val.length >= 2 && (val.startsWith("-") || val.startsWith("/"))) { let arg = val.substring(1, val.length); if (arg.startsWith("-")) { arg = arg.substring(1, arg.length); } switch (arg) { case "h": case "?": case "help": _mode = APP_MODE_ERRORHELP; break; case "cmd": case "c": if (i < process.argv.length - 1) { i++; _oper = process.argv[i]; } break; case "i": if (i < process.argv.length - 1) { i++; _inputPath = process.argv[i]; } break; case "outpath": case "o": case "out": if (i < process.argv.length - 1) { i++; _outPath = process.argv[i]; } break; case "storage": if (i < process.argv.length - 1) { i++; _customStoragePath = process.argv[i]; } break; default: break; } } else { // Only treat as input path if it's the first user argument (after main script) // and it's not the "." argument used in dev mode if (i === argsStartIndex && process.argv[i] !== ".") { _inputPath = process.argv[i]; } } } if (_oper != null) { switch (_oper.toLowerCase()) { case "help": _mode = APP_MODE_ERRORHELP; break; case "exportworld": if (_inputPath !== null && _outPath !== null) { Log_1.default.debug("Running export world command - from '" + _inputPath + "' to '" + _outPath + "'"); _mode = APP_MODE_EXPORTWORLD; } break; default: break; } if (_mode === APP_MODE_MAINAPP) { _mode = APP_MODE_ERRORHELP; } } if (_mode === APP_MODE_ERRORHELP) { console.log("Usage: mct <input path> -cmd [command name] <additional arguments>"); console.log("\nArguments:"); console.log(" <input path> Path to the input file or folder"); console.log("\nCommands:"); console.log(" server"); console.log(" Runs the app in server management only mode."); console.log(""); console.log(" exportworld"); console.log(" Exports a starter world for a given behavior pack."); console.log(" Arguments for 'exportworld' commands"); console.log(" -folder=<inputFolderPath> Input folder to read from"); console.log(" -world=<output MC World file> MCWorld file to export"); console.log(""); electron_1.app.quit(); electron_1.app.exit(0); } else if (_mode !== APP_MODE_EXIT) { electron_1.app.whenReady().then(() => { if (!_appIsReady) { electron_1.session.defaultSession.webRequest.onBeforeSendHeaders({ urls: ["*://*.npmjs.org/*"], }, (details, callback) => { details.requestHeaders["Origin"] = ""; callback({ requestHeaders: details.requestHeaders }); }); } _appIsReady = true; _startWindow(); }); } } function _startWindow() { if (_creatorToolsIsReady && _appIsReady && _mode === APP_MODE_MAINAPP) { _createTrayIcon(); _createMainAppWindow(); _contentLogger?.watchMinecraftReleaseFolder(); if (isDev) { installExtension(REACT_DEVELOPER_TOOLS) .then((name) => Log_1.default.debug("Added Extension: " + JSON.stringify(name))) .catch((error) => Log_1.default.error("An error occurred: " + error)); } _registerShortcuts(); } } function _createMainAppWindow() { const x = _creatorTools.windowX; const y = _creatorTools.windowY; let center = false; if (x === 0 && y === 0) { center = true; } const isMac = process.platform === "darwin"; _mainWindow = new electron_1.BrowserWindow({ width: _creatorTools.windowWidth, height: _creatorTools.windowHeight, x: x, y: y, center: center, frame: false, titleBarStyle: isMac ? "hiddenInset" : "hidden", trafficLightPosition: isMac ? { x: 16, y: 12 } : undefined, webPreferences: { nodeIntegration: false, nodeIntegrationInSubFrames: false, preload: path.join(__dirname, "preload.js"), nodeIntegrationInWorker: false, contextIsolation: true, }, icon: path.join(__dirname, "..", "..", "..", "build", "favicon.ico"), }); if (_creatorTools.windowState === 2) { _mainWindow.maximize(); } if (_commandHandler === null) { _commandHandler = new WindowCommandHandler_1.default(_mainWindow, electron_1.ipcMain, electron_1.screen, _creatorTools); _commandHandler.register(); } if (_dedicatedServerCommandHandler === null) { _dedicatedServerCommandHandler = new DedicatedServerCommandHandler_1.default(_mainWindow, electron_1.ipcMain, _env, _creatorTools, _utils); } if (_webSocketCommandHandler === null) { _webSocketCommandHandler = new WebSocketCommandHandler_1.default(_mainWindow, electron_1.ipcMain, _env); } if (_fileSystemCommandHandler === null) { _fileSystemCommandHandler = new FileSystemCommandHandler_1.default(_mainWindow, electron_1.ipcMain, _creatorTools, _env, _utils); } if (_contentSourceManager === null) { _contentSourceManager = new ContentSourceManager_1.default(_mainWindow, electron_1.ipcMain, _env, _utils); } if (_localCommandHandler === null) { _localCommandHandler = new LocalCommandHandler_1.default(_mainWindow, electron_1.ipcMain, _env, _utils); } _commandHandler.window = _mainWindow; let indexHtmlPath = isDev ? `${devUrl}?debug=true` : `file://${path.join(__dirname, "..", "..", "..", "build", "index.html")}`; let addedHash = false; if (_inputPath !== null) { if (!addedHash) { indexHtmlPath += "#"; addedHash = true; } else { indexHtmlPath += "&"; } // Resolve input path relative to app root (3 levels up from toolbuild/jsn/electron/) const appRoot = path.join(__dirname, "..", "..", ".."); let currentPath = path.resolve(appRoot, _inputPath); if (fs.existsSync(currentPath)) { const statResult = fs.statSync(currentPath); currentPath = StorageUtilities_1.default.canonicalizePath(currentPath); if (statResult.isDirectory()) { currentPath = StorageUtilities_1.default.ensureEndsWithDelimiter(currentPath); } const splitPath = StorageUtilities_1.default.getRootAndFocusPathFromInputPath(currentPath); const targetPath = StorageUtilities_1.default.ensureEndsWithDelimiter("<pt_" + _utils.ensureMappingForPath(splitPath.basePath) + ">") + (splitPath.focusPath ? StorageUtilities_1.default.ensureNotStartsWithDelimiter(splitPath.focusPath) : ""); indexHtmlPath += "input=" + encodeURIComponent(targetPath); } else { Log_1.default.debug("Input path does not exist appear to exist: " + currentPath); } } _mainWindow.loadURL(indexHtmlPath); if (isDev) { _mainWindow.webContents.openDevTools({ mode: "detach" }); } else { electron_1.Menu.setApplicationMenu(null); } _mainWindow.webContents.setWindowOpenHandler(({ url }) => { if (url.toLowerCase().startsWith("https://")) { electron_1.shell.openExternal(url); } return { action: "deny" }; }); } function _createTrayIcon() { try { const iconName = process.platform === "win32" ? "favicon.ico" : "favicon-32x32.png"; const iconPath = path.join(__dirname, "..", "..", "..", "build", iconName); _tray = new electron_1.Tray(iconPath); _tray.on("click", _handleTrayClick); _tray.setToolTip("Minecraft Creator Tools"); _rebuildTrayMenu(); } catch (err) { Log_1.default.debug("Failed to create tray icon: " + err.message); _tray = null; } } function _rebuildTrayMenu() { if (!_creatorTools || !_tray) { return; } const tools = []; let addedCustomSep = false; tools.push({ id: "deploy", label: "Deploy", click: _handleDeployClick }); tools.push({ id: "sepA", type: "separator" }); tools.push({ id: "pinToTop", label: "Pin to top", click: _pinToTop, type: "checkbox" }); tools.push({ id: "viewMode", label: "Change View Mode", click: _changeViewMode }); for (let i = 0; i < 9; i++) { const ctool = _creatorTools.getCustomTool(i); if (ctool && ctool.text) { let name = ctool.name; if (!name) { name = "Tool " + (i + 1).toString(); } let command = undefined; switch (i) { case 1: command = _sendCommand1; break; case 2: command = _sendCommand2; break; case 3: command = _sendCommand3; break; case 4: command = _sendCommand4; break; case 5: command = _sendCommand5; break; case 6: command = _sendCommand6; break; case 7: command = _sendCommand7; break; case 8: command = _sendCommand8; break; case 9: command = _sendCommand9; break; default: command = _sendCommand0; } if (!addedCustomSep) { tools.push({ id: "sepB", type: "separator" }); addedCustomSep = true; } tools.push({ id: "tool" + i, label: "Ctrl-" + (i + 1).toString() + ": " + name, click: command, }); } } _tray.setContextMenu(electron_1.Menu.buildFromTemplate(tools)); } function _registerShortcuts() { electron_1.globalShortcut.register("CommandOrControl+`", _sendFocus); electron_1.globalShortcut.register("CommandOrControl+1", _sendCommand1); electron_1.globalShortcut.register("CommandOrControl+2", _sendCommand2); electron_1.globalShortcut.register("CommandOrControl+3", _sendCommand3); electron_1.globalShortcut.register("CommandOrControl+4", _sendCommand4); electron_1.globalShortcut.register("CommandOrControl+5", _sendCommand5); electron_1.globalShortcut.register("CommandOrControl+6", _sendCommand6); electron_1.globalShortcut.register("CommandOrControl+7", _sendCommand7); electron_1.globalShortcut.register("CommandOrControl+8", _sendCommand8); electron_1.globalShortcut.register("CommandOrControl+9", _sendCommand9); electron_1.globalShortcut.register("CommandOrControl+0", _sendCommand0); } function _sendFocus() { _mainWindow?.focus(); } function _sendCommand1() { _sendCommand(1); } function _sendCommand2() { _sendCommand(2); } function _sendCommand3() { _sendCommand(3); } function _sendCommand4() { _sendCommand(4); } function _sendCommand5() { _sendCommand(5); } function _sendCommand6() { _sendCommand(6); } function _sendCommand7() { _sendCommand(7); } function _sendCommand8() { _sendCommand(8); } function _sendCommand9() { _sendCommand(9); } function _sendCommand0() { _sendCommand(10); } let _isPinnedToTop = false; function _pinToTop() { _isPinnedToTop = !_isPinnedToTop; let level = "normal"; if (_isPinnedToTop) { level = "screen-saver"; } _mainWindow?.setAlwaysOnTop(_isPinnedToTop, level); } function _changeViewMode() { } function _sendCommand(index) { if (_mainWindow !== null && _mainWindow !== undefined) { _mainWindow.webContents.send("appsvc", "externalKeyPress|command" + index); } } function _handleTrayClick() { if (_mainWindow === null) { return; } _mainWindow.isVisible() ? _mainWindow.hide() : _mainWindow.show(); } function _handleDeployClick(_menuItem, _window, _event) { Log_1.default.debug("Deploying.."); } async function minecraftShell(_event, data) { const slargs = data.split("|"); const command = slargs[1]; Log_1.default.debug("Running command: minecraft://" + command); (0, open_1.default)("minecraft://" + command); _mainWindow?.webContents.send("appsvc", "asyncminecraftShellComplete|" + slargs[0] + "|"); } if (_mode !== APP_MODE_ERRORHELP && _mode !== APP_MODE_EXIT) { electron_1.ipcMain.handle("asyncminecraftShell", minecraftShell); async function selectDirectory(_event, data) { const slargs = data.split("|"); const results = await electron_1.dialog.showOpenDialog(_mainWindow, { properties: ["openDirectory"], }); const tokPaths = []; if (results.filePaths) { for (const filePath of results.filePaths) { tokPaths.push("<pt_" + _utils.ensureMappingForPath(filePath) + ">"); } } _mainWindow?.webContents.send("appsvc", "asyncselectDirectoryComplete|" + slargs[0] + "|" + tokPaths); } electron_1.ipcMain.handle("asyncselectDirectory", selectDirectory); electron_1.app.on("window-all-closed", () => { if (process.platform !== "darwin") { electron_1.app.quit(); } }); electron_1.app.on("activate", () => { if (electron_1.BrowserWindow.getAllWindows().length === 0) { _createMainAppWindow(); } }); async function recycleItem(_event, data) { const slargs = data.split("|"); const itemPath = _utils.deTokenizePath(slargs[1]); if (!itemPath) { _mainWindow?.webContents.send("appsvc", "asyncshellRecycleItemComplete|" + slargs[0] + "|Invalid path"); return; } _utils.validateFilePath(itemPath); Log_1.default.debug("Recycling item '" + itemPath + "'"); let result = ""; try { await electron_1.shell.trashItem(itemPath); } catch (e) { result = e.toString(); } _mainWindow?.webContents.send("appsvc", "asyncshellRecycleItemComplete|" + slargs[0] + "|" + result); } electron_1.ipcMain.handle("asyncshellRecycleItem", recycleItem); async function reloadMct(_event, data) { const slargs = data.split("|"); await _creatorTools.load(true); _rebuildTrayMenu(); _mainWindow?.webContents.send("appsvc", "asyncreloadMctComplete|" + slargs[0] + "|"); } electron_1.ipcMain.handle("asyncreloadMct", reloadMct); async function openPath(_event, data) { const slargs = data.split("|"); const itemPath = _utils.deTokenizePath(slargs[1]); if (!itemPath) { _mainWindow?.webContents.send("appsvc", "asyncshellOpenPathComplete|" + slargs[0] + "|Invalid path"); return; } _utils.validateExecutableFilePath(itemPath); const result = await electron_1.shell.openPath(itemPath); _mainWindow?.webContents.send("appsvc", "asyncshellOpenPathComplete|" + slargs[0] + "|" + result); } electron_1.ipcMain.handle("asyncshellOpenPath", openPath); async function openFolderInExplorer(_event, data) { const slargs = data.split("|"); const folderPath = _utils.deTokenizePath(slargs[1]); if (!folderPath) { _mainWindow?.webContents.send("appsvc", "asyncshellOpenFolderInExplorerComplete|" + slargs[0] + "|"); return; } const result = await electron_1.shell.openPath(folderPath); const tok = _utils.ensureMappingForPath(result); let resultStr = "asyncshellOpenFolderInExplorerComplete|" + slargs[0] + "|<pt_" + tok + ">"; _mainWindow?.webContents.send("appsvc", resultStr); } electron_1.ipcMain.handle("asyncshellOpenFolderInExplorer", openFolderInExplorer); async function logToConsole(_event, data) { const firstPipe = data.indexOf("|"); if (firstPipe >= 0) { Log_1.default.debug("UX: " + data.substring(firstPipe + 1, data.length)); } _mainWindow?.webContents.send("appsvc", "asynclogToConsoleComplete|" + data[0] + "|"); } electron_1.ipcMain.handle("asynclogToConsole", logToConsole); }