forked from phcode-dev/staging.phcode.dev
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathshell.js
More file actions
1 lines (1 loc) · 22.5 KB
/
shell.js
File metadata and controls
1 lines (1 loc) · 22.5 KB
1
import initVFS from"./init_vfs.js";import ERR_CODES from"./errno.js";import{LRUCache}from"../thirdparty/no-minify/lru-cache.js";import*as Emmet from"../thirdparty/emmet.es.js";import{initTrustRing}from"./trust_ring.js";initTrustRing().catch(console.error),initVFS();const MAX_ALLOWED_TAURI_WINDOWS=30,CLI_ARGS_QUERY_PARAM="CLI_ARGS",CLI_CWD_QUERY_PARAM="CLI_CWD";let cliArgs,cliCWD,singleInstanceCLIHandler,quitTimeAppUpdateHandler,closeHandlerCb;const PHOENIX_WINDOW_PREFIX="phcode-",PHOENIX_EXTENSION_WINDOW_PREFIX="extn-";async function _getTauriWindowLabel(prefix){const tauriWindowLabels=await window.__TAURI__.invoke("_get_window_labels"),windowLabels={};for(let label of tauriWindowLabels)label.startsWith(prefix)&&(windowLabels[label]=!0);for(let i=1;i<=MAX_ALLOWED_TAURI_WINDOWS;i++){const windowLabel=`${prefix}${i}`;if(!windowLabels[windowLabel])return windowLabel}throw new Error("Could not get a free window label to create tauri window")}async function openURLInPhoenixWindow(url,{windowTitle:windowTitle,fullscreen:fullscreen,resizable:resizable,height:height,minHeight:minHeight,width:width,minWidth:minWidth,acceptFirstMouse:acceptFirstMouse,preferTabs:preferTabs,_prefixPvt:_prefixPvt=PHOENIX_EXTENSION_WINDOW_PREFIX}={}){const defaultHeight=900,defaultWidth=1366;if(window.__TAURI__){const windowLabel=await _getTauriWindowLabel(_prefixPvt),tauriWindow=new window.__TAURI__.window.WebviewWindow(windowLabel,{url:url,title:windowTitle||windowLabel||url,fullscreen:fullscreen,resizable:void 0===resizable||resizable,height:height||900,minHeight:minHeight||600,width:width||1366,minWidth:minWidth||800,acceptFirstMouse:void 0===acceptFirstMouse||acceptFirstMouse,fileDropEnabled:!1});return tauriWindow.isNativeWindow=!0,tauriWindow}if(window.__ELECTRON__){const label=await window.electronAPI.createPhoenixWindow(url,{windowTitle:windowTitle,fullscreen:fullscreen,resizable:void 0===resizable||resizable,height:height||900,minHeight:minHeight||600,width:width||1366,minWidth:minWidth||800,isExtension:_prefixPvt===PHOENIX_EXTENSION_WINDOW_PREFIX});return{label:label,isNativeWindow:!0}}let features="toolbar=no,location=no, status=no, menubar=no, scrollbars=yes";features=`${features}, width=${width||1366}, height=${height||900}`,(void 0===resizable||resizable)&&(features+=", resizable=yes"),preferTabs&&(features="");const nativeWindow=window.open(url,"_blank",features);return nativeWindow.isNativeWindow=!1,nativeWindow}function _resolveRect(rectOrNodeOrSelector){if(null==rectOrNodeOrSelector)return;let element;if("string"==typeof rectOrNodeOrSelector){const $el=$(rectOrNodeOrSelector);if(0===$el.length)throw new Error("No element found for selector: "+rectOrNodeOrSelector);if($el.length>1)throw new Error("Selector must match exactly one element, but matched "+$el.length+": "+rectOrNodeOrSelector);element=$el[0]}else{if(!(rectOrNodeOrSelector instanceof HTMLElement)){if("object"==typeof rectOrNodeOrSelector)return rectOrNodeOrSelector;throw new Error("Expected a rect object, DOM node, or jQuery selector string")}element=rectOrNodeOrSelector}const zoomFactor=window.PhStore&&window.PhStore.getItem("desktopZoomScale")||1,domRect=element.getBoundingClientRect();return{x:Math.round(domRect.x*zoomFactor),y:Math.round(domRect.y*zoomFactor),width:Math.round(domRect.width*zoomFactor),height:Math.round(domRect.height*zoomFactor)}}function _dataUrlToUint8Array(dataUrl){const base64=dataUrl.split(",")[1],binaryString=atob(base64),bytes=new Uint8Array(binaryString.length);for(let i=0;i<binaryString.length;i++)bytes[i]=binaryString.charCodeAt(i);return bytes}function _cropDataUrlToRect(dataUrl,rect){return new Promise((resolve,reject)=>{const img=new Image;img.onload=function(){try{const dpr=window._origDevicePixelRatio||window.devicePixelRatio||1,canvas=document.createElement("canvas"),sx=Math.round(rect.x*dpr),sy=Math.round(rect.y*dpr),sw=Math.round(rect.width*dpr),sh=Math.round(rect.height*dpr);canvas.width=sw,canvas.height=sh;const ctx=canvas.getContext("2d");ctx.drawImage(img,sx,sy,sw,sh,0,0,sw,sh),canvas.toBlob(function(blob){if(!blob)return void reject(new Error("Failed to crop screenshot to blob"));const reader=new FileReader;reader.onloadend=function(){resolve(new Uint8Array(reader.result))},reader.onerror=function(){reject(new Error("Failed to read cropped screenshot blob"))},reader.readAsArrayBuffer(blob)},"image/png")}catch(e){reject(e)}},img.onerror=function(){reject(new Error("Failed to load screenshot image for cropping"))},img.src=dataUrl})}Phoenix.libs={LRUCache:LRUCache,Emmet:{expand:Emmet.default,module:Emmet},hljs:window.hljs,iconv:fs.utils.iconv,picomatch:fs.utils.picomatch},Phoenix.globalAPI={};let _screenshotRequestId=0;function _requestExtensionScreenshot(){return new Promise((resolve,reject)=>{const id=++_screenshotRequestId,TIMEOUT_MS=3e4;let timeoutHandle;function onMessage(event){event.source===window&&event.data&&"phoenix_screenshot_response"===event.data.type&&event.data.id===id&&(window.removeEventListener("message",onMessage),clearTimeout(timeoutHandle),event.data.success?resolve(event.data.dataUrl):reject(new Error(event.data.error||"Screenshot capture failed")))}window.addEventListener("message",onMessage),timeoutHandle=setTimeout(()=>{window.removeEventListener("message",onMessage),reject(new Error("Screenshot capture timed out after 30 seconds"))},3e4),window.postMessage({type:"phoenix_screenshot_request",id:id},"*")})}async function _capturePageBinary(rectOrNodeOrSelector){const rect=_resolveRect(rectOrNodeOrSelector);if(void 0!==rect){if(void 0===rect.x||void 0===rect.y||void 0===rect.width||void 0===rect.height)throw new Error("rect must include all fields: x, y, width, height");if("number"!=typeof rect.x||"number"!=typeof rect.y||"number"!=typeof rect.width||"number"!=typeof rect.height)throw new Error("rect fields x, y, width, height must be numbers");if(rect.x<0||rect.y<0||rect.width<0||rect.height<0)throw new Error("rect fields x, y, width, height must be non-negative");if(rect.width<=0||rect.height<=0)throw new Error("rect width and height must be greater than 0");const zoomFactor=window.PhStore&&window.PhStore.getItem("desktopZoomScale")||1,maxWidth=Math.ceil(window.innerWidth*zoomFactor),maxHeight=Math.ceil(window.innerHeight*zoomFactor);if(rect.x+rect.width>maxWidth)throw new Error("rect x + width exceeds window innerWidth");if(rect.y+rect.height>maxHeight)throw new Error("rect y + height exceeds window innerHeight")}if(window.__TAURI__){const bytes=await window.__TAURI__.invoke("capture_page",{rect:rect});return new Uint8Array(bytes)}if(window.__ELECTRON__)return window.electronAPI.capturePage(rect);if(window._phoenixScreenshotExtensionAvailable){const dataUrl=await _requestExtensionScreenshot();return rect?_cropDataUrlToRect(dataUrl,rect):_dataUrlToUint8Array(dataUrl)}throw new Error("Screenshot capture is not supported in browsers. Install the Phoenix Code Screenshot extension for Chrome: load it as an unpacked extension from phoenix-builder-mcp/chrome_extension/ in chrome://extensions with Developer mode enabled.")}Phoenix.app={getNodeState:function(cbfn){cbfn(new Error("Node cannot be run in phoenix browser mode"))},getProcessID:function(){if(!Phoenix.isNativeApp)throw new Error("getProcessID is not supported in browsers");return window.__TAURI__?window.__TAURI__.invoke("get_process_id"):window.__ELECTRON__?window.electronAPI.getProcessId():void 0},registerQuitTimeAppUpdateHandler:function(handler){if(!Phoenix.isNativeApp)throw new Error("registerQuitTimeAppUpdateHandler is not supported in browsers");quitTimeAppUpdateHandler=handler},toggleDevtools:async function(){if(!Phoenix.isNativeApp)throw new Error("toggle_devtools is not supported in browsers");return window.__TAURI__?window.__TAURI__.invoke("toggle_devtools",{}):window.__ELECTRON__?window.electronAPI.toggleDevTools():void 0},onCloseWindowRequested:function(_closeHandlerCb){if("function"!=typeof _closeHandlerCb)throw new Error("onCloseWindowRequested callback must be a function!");if(closeHandlerCb)throw new Error("onCloseWindowRequested can only be registered once!");closeHandlerCb=_closeHandlerCb,window.__TAURI__?window.__TAURI__.window.appWindow.onCloseRequested(event=>{const shouldClose=closeHandlerCb();shouldClose||event.preventDefault()}):window.__ELECTRON__&&(window.electronAPI.registerCloseHandler(),window.electronAPI.onCloseRequested(()=>{const shouldClose=closeHandlerCb();shouldClose&&window.electronAPI.allowClose()}))},closeWindow:async function(forceClose){if(!Phoenix.isNativeApp)throw new Error("closeWindow is not supported in browsers");let instanceCount=0,extensionWindowCount=0;try{let allWindowLabels;instanceCount=await Phoenix.app.getPhoenixInstanceCount(),window.__TAURI__?allWindowLabels=await window.__TAURI__.invoke("_get_window_labels"):window.__ELECTRON__&&(allWindowLabels=await window.electronAPI.getWindowLabels());for(let windowLabel of allWindowLabels)windowLabel&&windowLabel.startsWith(PHOENIX_EXTENSION_WINDOW_PREFIX)&&extensionWindowCount++}catch(e){console.error("Ignoring Error while Phoenix.app.closeWindow: ",e)}if(1!==instanceCount||extensionWindowCount)window.__TAURI__?window.__TAURI__.window.getCurrent().close():window.__ELECTRON__&&window.electronAPI.closeWindow();else{if(!forceClose&&quitTimeAppUpdateHandler)try{await quitTimeAppUpdateHandler()}catch(e){console.error(e)}window.__TAURI__?window.__TAURI__.process.exit(0):window.__ELECTRON__&&window.electronAPI.quitApp(0)}},focusWindow:function(){if(!Phoenix.isNativeApp)return Promise.reject(new Error("focusWindow is not supported in browsers"));if(window.__TAURI__)window.__TAURI__.window.getCurrent().setAlwaysOnTop(!0),window.__TAURI__.window.getCurrent().setFocus(),window.__TAURI__.window.getCurrent().setAlwaysOnTop(!1);else if(window.__ELECTRON__)return window.electronAPI.focusWindow()},closeWindowByLabel:async function(label){if(!Phoenix.isNativeApp)throw new Error("closeWindowByLabel is not supported in browsers");if(window.__TAURI__){const win=window.__TAURI__.window.WebviewWindow.getByLabel(label);return!!win&&(await win.close(),!0)}return!!window.__ELECTRON__&&window.electronAPI.closeWindowByLabel(label)},getCommandLineArgs:async function(){if(!Phoenix.isNativeApp)return null;const phoenixURL=new URL(location.href),cliQueryParam=phoenixURL.searchParams.get("CLI_ARGS");cliQueryParam&&(cliArgs=JSON.parse(decodeURIComponent(cliQueryParam)));const cliCWDQueryParam=phoenixURL.searchParams.get("CLI_CWD");return cliCWDQueryParam&&(cliCWD=JSON.parse(decodeURIComponent(cliCWDQueryParam))),cliArgs?{cwd:cliCWD,args:cliArgs}:(cliArgs=null,window.__TAURI__?(cliCWD=await window.__TAURI__.invoke("get_current_working_dir"),cliArgs=await window.__TAURI__.invoke("_get_commandline_args")):window.__ELECTRON__&&(cliCWD=await window.electronAPI.getCwd(),cliArgs=await window.electronAppAPI.getCliArgs()),{cwd:cliCWD,args:cliArgs})},setSingleInstanceCLIArgsHandler:function(handlerFn){if(singleInstanceCLIHandler)throw new Error("A single instance handler is already registered!");handlerFn&&(singleInstanceCLIHandler=handlerFn),window.__TAURI__?(window.__TAURI__.event.listen("single-instance",({payload:payload})=>{handlerFn(payload.args,payload.cwd)}),window.__TAURI__.event.listen("scheme-request-received",receivedEvent=>{console.log("Macos received Event from OS:",receivedEvent);const fileURL=receivedEvent.payload,fileURLArray=receivedEvent.payload.fileURLArray;window.__TAURI__.tauri.invoke("get_mac_deep_link_requests");const eventToUse=["macOSEvent"];if("string"==typeof fileURL)eventToUse.push(decodeURIComponent(fileURL.replace("file://","")));else if(fileURLArray)for(let fileUrlEntry of fileURLArray)eventToUse.push(decodeURIComponent(fileUrlEntry.replace("file://","")));handlerFn(eventToUse,"")}),window.__TAURI__.tauri.invoke("get_mac_deep_link_requests").then(filesURLList=>{filesURLList.length&&Phoenix.app.isPrimaryDesktopPhoenixWindow().then(isPrimary=>{if(isPrimary){const eventToUse=["macOSEvent"];for(let fileUrlEntry of filesURLList)eventToUse.push(decodeURIComponent(fileUrlEntry.replace("file://","")));handlerFn(eventToUse,"")}else window.__TAURI__.event.emit("scheme-request-received",{fileURLArray:filesURLList})})})):window.__ELECTRON__&&window.electronAPI.onSingleInstance(payload=>{handlerFn(payload.args,payload.cwd)})},clipboardReadText:function(){return window.__TAURI__?window.__TAURI__.clipboard.readText():window.__ELECTRON__?window.electronAPI.clipboardReadText():window.navigator&&window.navigator.clipboard?window.navigator.clipboard.readText():Promise.reject(new Error("clipboardReadText: Not supported."))},clipboardReadFiles:function(){return new Promise((resolve,reject)=>{let filesPromise;if(window.__TAURI__)filesPromise=window.__TAURI__.tauri.invoke("_get_clipboard_files");else{if(!window.__ELECTRON__)return void resolve();filesPromise=window.electronAPI.clipboardReadFiles()}filesPromise.then(files=>{if(!files)return void resolve(files);const vfsPaths=[];for(let platformPath of files)vfsPaths.push(Phoenix.VFS.getTauriVirtualPath(platformPath));resolve(vfsPaths)}).catch(reject)})},copyToClipboard:function(textToCopy){if(window.__TAURI__)return window.__TAURI__.clipboard.writeText(textToCopy);if(window.__ELECTRON__)return window.electronAPI.clipboardWriteText(textToCopy);if(window.navigator&&window.navigator.clipboard)return window.navigator.clipboard.writeText(textToCopy);const textArea=document.createElement("textarea");return textArea.value=textToCopy,document.body.appendChild(textArea),textArea.select(),document.execCommand("copy"),document.body.removeChild(textArea),Promise.resolve()},isFullscreen:function(){return Phoenix.isNativeApp?window.__TAURI__?window.__TAURI__.window.appWindow.isFullscreen():window.__ELECTRON__?window.electronAPI.isFullscreen():void 0:Promise.resolve(!!document.fullscreenElement)},setFullscreen:function(enable){return Phoenix.isNativeApp?window.__TAURI__?window.__TAURI__.window.appWindow.setFullscreen(enable):window.__ELECTRON__?window.electronAPI.setFullscreen(enable):void 0:enable?document.documentElement.requestFullscreen():document.exitFullscreen?document.exitFullscreen():Promise.resolve()},getDisplayLocation:function(fullVFSPath){return fullVFSPath.startsWith(Phoenix.VFS.getTauriDir())?Phoenix.fs.getTauriPlatformPath(fullVFSPath):fullVFSPath.startsWith(Phoenix.VFS.getMountDir())?fullVFSPath.replace(Phoenix.VFS.getMountDir(),""):`${path.basename(fullVFSPath)} - ${window.Strings.STORED_IN_YOUR_BROWSER}`},getDisplayPath:function(fullOrRelativeVFSPath){return fullOrRelativeVFSPath?fullOrRelativeVFSPath.startsWith(Phoenix.VFS.getTauriDir())?Phoenix.fs.getTauriPlatformPath(fullOrRelativeVFSPath):fullOrRelativeVFSPath.startsWith(Phoenix.VFS.getMountDir())?fullOrRelativeVFSPath.replace(Phoenix.VFS.getMountDir(),""):fullOrRelativeVFSPath:""},canMoveToTrash:function(fullVFSPath){return!!(Phoenix.isNativeApp&&fullVFSPath&&fullVFSPath.startsWith(Phoenix.VFS.getTauriDir()))},moveToTrash:async function(fullVFSPath){if(!Phoenix.isNativeApp)throw new Error("moveToTrash is not supported in browsers");if(!fullVFSPath)throw new Error("Please specify a path to move to trash");if(!fullVFSPath.startsWith(Phoenix.VFS.getTauriDir()))throw new Error("moveToTrash only works with native paths, but got: "+fullVFSPath);const platformPath=Phoenix.fs.getTauriPlatformPath(fullVFSPath);return window.__TAURI__?window.__TAURI__.invoke("move_to_trash",{deletePath:platformPath}):window.__ELECTRON__?window.electronAPI.moveToTrash(platformPath):void 0},setWindowTitle:async function(title){window.document.title=title,window.__TAURI__?await window.__TAURI__.window.appWindow.setTitle(title):window.__ELECTRON__&&await window.electronAPI.setWindowTitle(title)},getWindowTitle:async function(){return window.__TAURI__?window.__TAURI__.window.appWindow.title():window.__ELECTRON__?window.electronAPI.getWindowTitle():window.document.title},openPathInFileBrowser:function(fullVFSPath){return new Promise((resolve,reject)=>{if(!Phoenix.isNativeApp||!fullVFSPath.startsWith(Phoenix.VFS.getTauriDir()))return void reject("openPathInFileBrowser is only currently supported in Native builds for native paths!");if(fullVFSPath.toLowerCase().startsWith("http://")||fullVFSPath.toLowerCase().startsWith("https://")||fullVFSPath.toLowerCase().startsWith("file://"))return void reject("Please use openURLInDefaultBrowser API to open URLs");const platformPath=Phoenix.fs.getTauriPlatformPath(fullVFSPath);window.__TAURI__?window.__TAURI__.tauri.invoke("show_in_folder",{path:platformPath}).then(resolve).catch(reject):window.__ELECTRON__&&(window.electronAPI.showInFolder(platformPath),resolve())})},openURLInDefaultBrowser:function(url,tabIdentifier="_blank"){return new Promise((resolve,reject)=>{Phoenix.isNativeApp?url.toLowerCase().startsWith("http://")||url.toLowerCase().startsWith("https://")?window.__TAURI__?window.__TAURI__.shell.open(url).then(resolve).catch(reject):window.__ELECTRON__&&window.electronAPI.openExternal(url).then(resolve).catch(reject):reject("openURLInDefaultBrowser: URL should be http or https, but was "+url):resolve(window.open(url,tabIdentifier,"noopener,noreferrer"))})},isPrimaryDesktopPhoenixWindow:async function(){if(!Phoenix.isNativeApp)return console.error("isPrimaryDesktopPhoenixWindow is not supported in browsers!"),!0;let currentWindowLabel,allWindowLabels;if(window.__TAURI__?(currentWindowLabel=window.__TAURI__.window.getCurrent().label,allWindowLabels=await window.__TAURI__.invoke("_get_window_labels")):window.__ELECTRON__&&(currentWindowLabel=await window.electronAPI.getCurrentWindowLabel(),allWindowLabels=await window.electronAPI.getWindowLabels()),"main"===currentWindowLabel)return!0;if(allWindowLabels.includes("main"))return!1;for(let windowLabel of allWindowLabels)if(windowLabel&&windowLabel.startsWith("phcode-")&¤tWindowLabel!==windowLabel&¤tWindowLabel>windowLabel)return!1;return!0},getPhoenixInstanceCount:async function(){if(!Phoenix.isNativeApp)return console.error("getPhoenixInstanceCount is not supported in browsers!"),1;let windowCount=0,allWindowLabels;window.__TAURI__?allWindowLabels=await window.__TAURI__.invoke("_get_window_labels"):window.__ELECTRON__&&(allWindowLabels=await window.electronAPI.getWindowLabels());for(let windowLabel of allWindowLabels)windowLabel&&(windowLabel.startsWith("phcode-")||"main"===windowLabel)&&windowCount++;return windowCount},getPlatformArch:async function(){return Phoenix.isNativeApp?window.__TAURI__?window.__TAURI__.os.arch():window.__ELECTRON__?window.electronAPI.getPlatformArch():void 0:(console.error("getPlatformArch is not supported in browsers!"),null)},openNewPhoenixEditorWindow:async function(preferredWidth,preferredHeight,_cliArgsArray,_cwd){const phoenixURL=new URL(location.href);if(_cliArgsArray){const cliVal=encodeURIComponent(JSON.stringify(_cliArgsArray));phoenixURL.searchParams.set("CLI_ARGS",cliVal)}else phoenixURL.searchParams.delete("CLI_ARGS");if(_cwd){const cliVal=encodeURIComponent(JSON.stringify(_cwd));phoenixURL.searchParams.set("CLI_CWD",cliVal)}else phoenixURL.searchParams.delete("CLI_CWD");await openURLInPhoenixWindow(phoenixURL.href,{width:preferredWidth,height:preferredHeight,preferTabs:!0,_prefixPvt:"phcode-"})},openURLInPhoenixWindow:openURLInPhoenixWindow,zoomWebView:function(scaleFactor=1){if(!Phoenix.isNativeApp)throw new Error("zoomWebView is not supported in browsers");if(scaleFactor<.1||scaleFactor>2)throw new Error("zoomWebView scale factor should be between .1 and 2");return window.__TAURI__&&"mac"===Phoenix.platform&&(void 0===window._origDevicePixelRatio&&(window._origDevicePixelRatio=window.devicePixelRatio),Object.defineProperty(window,"devicePixelRatio",{get:()=>window._origDevicePixelRatio*scaleFactor,configurable:!0})),window.__TAURI__?window.__TAURI__.tauri.invoke("zoom_window",{scaleFactor:scaleFactor}):window.__ELECTRON__?window.electronAPI.zoomWindow(scaleFactor):void 0},getZoomFactor:function(){return window.PhStore&&window.PhStore.getItem("desktopZoomScale")||1},getWindowSize:async function(){if(window.__TAURI__){const currentWindow=window.__TAURI__.window.getCurrent(),outerSize=await currentWindow.outerSize(),innerSize=await currentWindow.innerSize();let size=outerSize;"mac"!==Phoenix.platform&&(size=innerSize);const scaleFactor=await currentWindow.scaleFactor();return{width:Math.round(size.width/scaleFactor),height:Math.round(size.height/scaleFactor)}}return window.__ELECTRON__?{width:window.outerWidth,height:window.outerHeight}:{width:window.innerWidth,height:window.innerHeight}},_openUrlInBrowserWin:function(url,browser){if(!Phoenix.isNativeApp)throw new Error("_openUrlInBrowserWin is not supported in browsers");if("win"!==Phoenix.platform)throw new Error("_openUrlInBrowserWin is only supported in windows");return window.__TAURI__?window.__TAURI__.invoke("_open_url_in_browser_win",{url:url,browser:browser}):window.__ELECTRON__?window.electronAPI.openUrlInBrowserWin(url,browser):void 0},getApplicationSupportDirectory:Phoenix.VFS.getAppSupportDir,getExtensionsDirectory:Phoenix.VFS.getExtensionDir,getUserDocumentsDirectory:Phoenix.VFS.getUserDocumentsDirectory,getUserProjectsDirectory:Phoenix.VFS.getUserProjectsDirectory,getTempDirectory:Phoenix.VFS.getTempDir,ERR_CODES:ERR_CODES,getTimeSinceStartup:function(){return Date.now()-Phoenix.startTime},language:navigator.language,emitToAllWindows:async function(eventName,payload){if(!Phoenix.isNativeApp)throw new Error("emitToAllWindows is not supported in browsers");return window.__TAURI__?window.__TAURI__.event.emit(eventName,payload):window.__ELECTRON__?window.electronAPI.emitToAllWindows(eventName,payload):void 0},emitToWindow:async function(targetLabel,eventName,payload){if(!Phoenix.isNativeApp)throw new Error("emitToWindow is not supported in browsers");return window.__TAURI__?window.__TAURI__.event.emit(eventName,payload):!!window.__ELECTRON__&&window.electronAPI.emitToWindow(targetLabel,eventName,payload)},onWindowEvent:function(eventName,callback){if(!Phoenix.isNativeApp)throw new Error("onWindowEvent is not supported in browsers");if(window.__TAURI__){let unlisten=null;return window.__TAURI__.event.listen(eventName,event=>{callback(event.payload)}).then(fn=>{unlisten=fn}),()=>{unlisten&&unlisten()}}return window.__ELECTRON__?window.electronAPI.onWindowEvent(eventName,callback):()=>{}},screenShotBinary:function(rectOrNodeOrSelector){return _capturePageBinary(rectOrNodeOrSelector)},screenShotToBlob:async function(rectOrNodeOrSelector){const bytes=await _capturePageBinary(rectOrNodeOrSelector);return new Blob([bytes],{type:"image/png"})},screenShotToPNGFile:async function(filePathToSave,rectOrNodeOrSelector){if(!filePathToSave||"string"!=typeof filePathToSave)throw new Error("filePathToSave must be a non-empty string");const bytes=await _capturePageBinary(rectOrNodeOrSelector);return new Promise((resolve,reject)=>{fs.writeFile(filePathToSave,bytes.buffer,"binary",err=>{err?reject(err):resolve()})})}},window.appshell||(window.appshell=Phoenix);