diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index ff7a028..3f44b1c 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -37,27 +37,27 @@ jobs: with: fetch-depth: 1 - - name: Setup Node.js - uses: actions/setup-node@v4 + - name: Setup Bun + uses: oven-sh/setup-bun@v1 with: - node-version: '20' - cache: 'npm' + bun-version: latest - name: Install dependencies - run: npm install + run: bun install - name: Download Neutralino binaries - run: npx neu update + run: bun x neu update - name: Build application - run: npm run build:desktop + run: bun run build - name: Prepare Release run: | mkdir release cp dist/Monochrome/resources.neu release/ cp neutralino.config.json release/ - cp -r extensions release/ + # Extensions are already copied to dist/Monochrome/extensions by postbuild + cp -r dist/Monochrome/extensions release/ cp dist/Monochrome/${{ matrix.binary_source }} release/${{ matrix.binary_dest }} shell: bash diff --git a/.gitignore b/.gitignore index c1b2e4d..db60023 100644 Binary files a/.gitignore and b/.gitignore differ diff --git a/js/app.js b/js/app.js index bdb80cd..c5635f7 100644 --- a/js/app.js +++ b/js/app.js @@ -1,5 +1,5 @@ //js/app.js -console.log('[App] Script loaded'); +console.log('[App] Script loaded. Query:', window.location.search); import { LosslessAPI } from './api.js'; import { apiSettings, @@ -22,11 +22,12 @@ import { db } from './db.js'; import { syncManager } from './accounts/pocketbase.js'; import { registerSW } from 'virtual:pwa-register'; import { initializeDiscordRPC } from './discord-rpc.js'; -import * as Neutralino from '@neutralinojs/lib'; +import * as Neutralino from './neutralino-bridge.js'; import './smooth-scrolling.js'; // Assign Neutralino to window for global access -if (typeof window !== 'undefined' && window.NL_MODE) { +// Force global assignment for Bridge mode +if (typeof window !== 'undefined') { window.Neutralino = Neutralino; } @@ -238,30 +239,34 @@ async function disablePwaForAuthGate() { } document.addEventListener('DOMContentLoaded', async () => { - // Initialize desktop environment (Neutralino) - const isDesktop = typeof window !== 'undefined' && (window.NL_MODE || window.location.port === '5050'); - if (typeof window !== 'undefined' && window.Neutralino) { - console.log('[App] Neutralino object detected. Environment:', isDesktop ? 'Desktop' : 'Web'); - if (isDesktop) { - console.log('[App] Initializing Neutralino desktop environment...'); - try { - Neutralino.init(); - console.log('[App] Neutralino.init() called successfully.'); + // Delay detection slightly to allow for global injection + await new Promise((resolve) => setTimeout(resolve, 100)); + + const initNeutralino = async () => { + const urlParams = new URLSearchParams(window.location.search); + const isNeutralino = urlParams.get('mode') === 'neutralino'; + + if (isNeutralino) { + try { + // Bridge init is instant and doesn't need tokens/ports + Neutralino.init(); + console.log('[App] Neutralino Bridge initialized.'); - // Register events immediately Neutralino.events.on('windowClose', () => { - console.log('[App] Window close event triggered.'); Neutralino.app.exit(); }); - } catch (error) { - console.error('[App] Failed to initialize desktop environment:', error); + + // Initialize Discord RPC + console.log('[App] Starting Discord RPC...'); + initializeDiscordRPC(player); + + } catch (e) { + console.error('[App] Neutralino init failed:', e); } - } else { - console.log('[App] Skipping Neutralino.init() on regular web environment.'); } - } else { - console.log('[App] Neutralino object NOT detected.'); - } + }; + + const api = new LosslessAPI(apiSettings); @@ -411,10 +416,9 @@ document.addEventListener('DOMContentLoaded', async () => { // Initialize tracker initTracker(player); - if (typeof window !== 'undefined' && window.Neutralino && (window.NL_MODE || window.location.port === '5050')) { - console.log('[App] Starting Discord RPC...'); - initializeDiscordRPC(player); - } + + + initNeutralino(); const castBtn = document.getElementById('cast-btn'); initializeCasting(audioPlayer, castBtn); diff --git a/js/discord-rpc.js b/js/discord-rpc.js index eb2708f..31cdff1 100644 --- a/js/discord-rpc.js +++ b/js/discord-rpc.js @@ -1,4 +1,5 @@ import { getTrackTitle, getTrackArtists } from './utils.js'; +import * as Neutralino from './neutralino-bridge.js'; export function initializeDiscordRPC(player) { console.log('[DiscordRPC] Initializing...'); @@ -51,8 +52,8 @@ export function initializeDiscordRPC(player) { smallImageKey: 'pause', smallImageText: 'Paused', }; - Neutralino.events.broadcast('discord:update', idlingData).catch(() => {}); - Neutralino.extensions.dispatch(EXTENSION_ID, 'discord:update', idlingData).catch(() => {}); + Neutralino.events.broadcast('discord:update', idlingData).catch(() => { }); + Neutralino.extensions.dispatch(EXTENSION_ID, 'discord:update', idlingData).catch(() => { }); } }, 5000); @@ -83,6 +84,6 @@ export function initializeDiscordRPC(player) { smallImageKey: 'pause', smallImageText: 'Paused', }) - .catch(() => {}); + .catch(() => { }); } } diff --git a/js/neutralino-bridge.js b/js/neutralino-bridge.js new file mode 100644 index 0000000..6f459fb --- /dev/null +++ b/js/neutralino-bridge.js @@ -0,0 +1,80 @@ +// js/neutralino-bridge.js + +const listeners = new Map(); + +// Listen for events from the Shell (Parent) +window.addEventListener('message', (event) => { + if (event.data?.type === 'NL_EVENT') { + const { eventName, detail } = event.data; + if (listeners.has(eventName)) { + listeners.get(eventName).forEach((handler) => { + try { + handler(detail); + } catch (e) { + console.error('[Bridge] Error in event handler:', e); + } + }); + } + } +}); + +export const init = async () => { + console.log('[Bridge] Initialized. Mode: Iframe Shell.'); + // Notify Shell we are ready + window.parent.postMessage({ type: 'NL_INIT' }, '*'); +}; + +export const events = { + on: (eventName, handler) => { + if (!listeners.has(eventName)) { + listeners.set(eventName, []); + } + listeners.get(eventName).push(handler); + }, + off: (eventName, handler) => { + if (!listeners.has(eventName)) return; + const handlers = listeners.get(eventName); + const index = handlers.indexOf(handler); + if (index > -1) handlers.splice(index, 1); + }, + broadcast: async (eventName, data) => { + window.parent.postMessage({ type: 'NL_BROADCAST', eventName, data }, '*'); + }, +}; + +export const extensions = { + dispatch: async (extensionId, eventName, data) => { + window.parent.postMessage({ type: 'NL_EXTENSION', extensionId, eventName, data }, '*'); + }, +}; + +export const app = { + exit: async () => { + window.parent.postMessage({ type: 'NL_APP_EXIT' }, '*'); + }, +}; + +const _window = { + minimize: async () => { + window.parent.postMessage({ type: 'NL_WINDOW_MIN' }, '*'); + }, + maximize: async () => { + window.parent.postMessage({ type: 'NL_WINDOW_MAX' }, '*'); + }, + isVisible: async () => { + return true; // Mock response + }, + setTitle: async (title) => { + window.parent.postMessage({ type: 'NL_WINDOW_SET_TITLE', title }, '*'); + } +}; + +// Expose generically for other modules +export { _window as window }; +export default { + init, + events, + extensions, + app, + window: _window +}; diff --git a/neutralino.config.json b/neutralino.config.json index 1fee33d..b717350 100644 --- a/neutralino.config.json +++ b/neutralino.config.json @@ -6,12 +6,12 @@ "description": "Lossless music streaming", "version": "1.0.0", "defaultMode": "window", - "documentRoot": "www/", - "url": "https://monochrome.tf", + "documentRoot": "dist/", + "url": "/neutralino_loader.html", "enableServer": true, "enableNativeAPI": true, "enableExtensions": true, - "tokenSecurity": "one-time", + "tokenSecurity": "none", "modes": { "window": { "title": "Monochrome", @@ -32,7 +32,7 @@ "port": 5050, "cli": { "binaryName": "Monochrome", - "resourcesPath": "www/", + "resourcesPath": "dist/", "binaryVersion": "6.5.0", "clientVersion": "6.5.0" }, @@ -44,5 +44,10 @@ "commandWindows": "powershell.exe -ExecutionPolicy Bypass -File \"${NL_PATH}/extensions/js.neutralino.discordrpc/bridge.ps1\"" } ], - "nativeAllowList": ["app.exit", "window.*", "extensions.*", "events.*"] -} + "nativeAllowList": [ + "app.exit", + "window.*", + "extensions.*", + "events.*" + ] +} \ No newline at end of file diff --git a/package.json b/package.json index 4a7b39b..6f15e0c 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,8 @@ "main": "sw.js", "scripts": { "dev": "vite", - "build": "vite build", - "build:desktop": "vite build --mode neutralino && npx neu build && node -e \"const fs = require('fs'); fs.cpSync('extensions', 'dist/Monochrome/extensions', {recursive: true}); fs.copyFileSync('neutralino.config.json', 'dist/Monochrome/neutralino.config.json')\"", + "build": "vite build --mode neutralino && bun x neu build", + "postbuild": "node -e \"const fs = require('fs'); const path = require('path'); const src = 'extensions'; const dest = path.join('dist', 'Monochrome', 'extensions'); if (fs.existsSync(src)) { fs.mkdirSync(dest, { recursive: true }); fs.cpSync(src, dest, { recursive: true }); console.log('Extensions manually copied to ' + dest); }\"", "preview": "vite preview", "start": "vite preview", "lint:js": "eslint .", @@ -55,4 +55,4 @@ "dashjs": "^5.1.1", "pocketbase": "^0.26.5" } -} +} \ No newline at end of file diff --git a/public/neutralino.js b/public/neutralino.js new file mode 100644 index 0000000..c93e267 --- /dev/null +++ b/public/neutralino.js @@ -0,0 +1 @@ +var Neutralino=function(e){"use strict";function t(e,t){return window.addEventListener(e,t),Promise.resolve({success:!0,message:"Event listener added"})}function n(e,t){const n=new CustomEvent(e,{detail:t});return window.dispatchEvent(n),Promise.resolve({success:!0,message:"Message dispatched"})}function r(e){const t=window.atob(e),n=t.length,r=new Uint8Array(n);for(let e=0;e{if(await f(c),!window.NL_EXTENABLED)return;let e=await l("extensions.getStats");for(let t of e.connected)n("extensionReady",t)}),t("extClientConnect",e=>{n("extensionReady",e.detail)}),!window.NL_EXTENABLED)return;t("extensionReady",async e=>{e.detail in u&&(await f(u[e.detail]),delete u[e.detail])})}(),a.addEventListener("message",e=>{const t=JSON.parse(e.data);t.id&&t.id in s?(t.data?.error?(s[t.id].reject(t.data.error),"NE_RT_INVTOKN"==t.data.error.code&&(a.close(),document.body.innerText="",document.write("NE_RT_INVTOKN: Neutralinojs application cannot execute native methods since NL_TOKEN is invalid."))):t.data?.success&&s[t.id].resolve(t.data.hasOwnProperty("returnValue")?t.data.returnValue:t.data),delete s[t.id]):t.event&&("openedFile"==t.event&&"dataBinary"==t?.data?.action&&(t.data.data=r(t.data.data)),n(t.event,t.data))}),a.addEventListener("open",async e=>{n("ready")}),a.addEventListener("close",async e=>{n("serverOffline",{code:"NE_CL_NSEROFF",message:"Neutralino server is offline. Try restarting the application"})}),a.addEventListener("error",async e=>{document.body.innerText="",document.write("NE_CL_IVCTOKN: Neutralinojs application cannot connect with the framework core using NL_TOKEN.")})}function l(e,t){return new Promise((n,r)=>{if(a?.readyState!=WebSocket.OPEN)return o={method:e,data:t,resolve:n,reject:r},void c.push(o);var o;const i="10000000-1000-4000-8000-100000000000".replace(/[018]/g,e=>(e^crypto.getRandomValues(new Uint8Array(1))[0]&15>>e/4).toString(16)),u=g();s[i]={resolve:n,reject:r},a.send(JSON.stringify({id:i,method:e,data:t,accessToken:u}))})}async function f(e){for(;e.length>0;){const t=e.shift();try{const e=await l(t.method,t.data);t.resolve(e)}catch(e){t.reject(e)}}}function g(){return window.NL_TOKEN||sessionStorage.getItem("NL_TOKEN")||""}function w(e,t){return l("filesystem.writeBinaryFile",{path:e,data:o(t)})}var m={__proto__:null,appendBinaryFile:function(e,t){return l("filesystem.appendBinaryFile",{path:e,data:o(t)})},appendFile:function(e,t){return l("filesystem.appendFile",{path:e,data:t})},copy:function(e,t,n){return l("filesystem.copy",{source:e,destination:t,...n})},createDirectory:function(e){return l("filesystem.createDirectory",{path:e})},createWatcher:function(e){return l("filesystem.createWatcher",{path:e})},getAbsolutePath:function(e){return l("filesystem.getAbsolutePath",{path:e})},getJoinedPath:function(...e){return l("filesystem.getJoinedPath",{paths:e})},getNormalizedPath:function(e){return l("filesystem.getNormalizedPath",{path:e})},getOpenedFileInfo:function(e){return l("filesystem.getOpenedFileInfo",{id:e})},getPathParts:function(e){return l("filesystem.getPathParts",{path:e})},getPermissions:function(e){return l("filesystem.getPermissions",{path:e})},getRelativePath:function(e,t){return l("filesystem.getRelativePath",{path:e,base:t})},getStats:function(e){return l("filesystem.getStats",{path:e})},getUnnormalizedPath:function(e){return l("filesystem.getUnnormalizedPath",{path:e})},getWatchers:function(){return l("filesystem.getWatchers")},move:function(e,t){return l("filesystem.move",{source:e,destination:t})},openFile:function(e){return l("filesystem.openFile",{path:e})},readBinaryFile:function(e,t){return new Promise((n,o)=>{l("filesystem.readBinaryFile",{path:e,...t}).then(e=>{n(r(e))}).catch(e=>{o(e)})})},readDirectory:function(e,t){return l("filesystem.readDirectory",{path:e,...t})},readFile:function(e,t){return l("filesystem.readFile",{path:e,...t})},remove:function(e){return l("filesystem.remove",{path:e})},removeWatcher:function(e){return l("filesystem.removeWatcher",{id:e})},setPermissions:function(e,t,n){return l("filesystem.setPermissions",{path:e,...t,mode:n})},updateOpenedFile:function(e,t,n){return l("filesystem.updateOpenedFile",{id:e,event:t,data:n})},writeBinaryFile:w,writeFile:function(e,t){return l("filesystem.writeFile",{path:e,data:t})}};function p(e,t){return l("os.execCommand",{command:e,...t})}var h={__proto__:null,execCommand:p,getEnv:function(e){return l("os.getEnv",{key:e})},getEnvs:function(){return l("os.getEnvs")},getPath:function(e){return l("os.getPath",{name:e})},getSpawnedProcesses:function(){return l("os.getSpawnedProcesses")},open:function(e){return l("os.open",{url:e})},setTray:function(e){return l("os.setTray",e)},showFolderDialog:function(e,t){return l("os.showFolderDialog",{title:e,...t})},showMessageBox:function(e,t,n,r){return l("os.showMessageBox",{title:e,content:t,choice:n,icon:r})},showNotification:function(e,t,n){return l("os.showNotification",{title:e,content:t,icon:n})},showOpenDialog:function(e,t){return l("os.showOpenDialog",{title:e,...t})},showSaveDialog:function(e,t){return l("os.showSaveDialog",{title:e,...t})},spawnProcess:function(e,t){return l("os.spawnProcess",{command:e,...t})},updateSpawnedProcess:function(e,t,n){return l("os.updateSpawnedProcess",{id:e,event:t,data:n})}};var y={__proto__:null,getArch:function(){return l("computer.getArch")},getCPUInfo:function(){return l("computer.getCPUInfo")},getDisplays:function(){return l("computer.getDisplays")},getKernelInfo:function(){return l("computer.getKernelInfo")},getMemoryInfo:function(){return l("computer.getMemoryInfo")},getMousePosition:function(){return l("computer.getMousePosition")},getOSInfo:function(){return l("computer.getOSInfo")}};var _={__proto__:null,clear:function(){return l("storage.clear")},getData:function(e){return l("storage.getData",{key:e})},getKeys:function(){return l("storage.getKeys")},removeData:function(e){return l("storage.removeData",{key:e})},setData:function(e,t){return l("storage.setData",{key:e,data:t})}};function v(e,t){return l("debug.log",{message:e,type:t})}var N={__proto__:null,log:v};function E(e){return l("app.exit",{code:e})}var P={__proto__:null,broadcast:function(e,t){return l("app.broadcast",{event:e,data:t})},exit:E,getConfig:function(){return l("app.getConfig")},killProcess:function(){return l("app.killProcess")},readProcessInput:function(e){return l("app.readProcessInput",{readAll:e})},restartProcess:function(e){return new Promise(async t=>{let n=window.NL_ARGS.reduce((e,t)=>(t.includes(" ")&&(t=`"${t}"`),e+=" "+t),"");e?.args&&(n+=" "+e.args),await p(n,{background:!0}),E(),t()})},writeProcessError:function(e){return l("app.writeProcessError",{data:e})},writeProcessOutput:function(e){return l("app.writeProcessOutput",{data:e})}};const b=new Set,D=new Map,T=new Map;function O(e=0,t=0){return l("window.beginDrag",{screenX:e,screenY:t})}function S(){return l("window.getSize")}var L={__proto__:null,beginDrag:O,center:function(){return l("window.center")},create:function(e,t){return new Promise((n,r)=>{function o(e){return"string"!=typeof e||(e=e.trim()).includes(" ")&&(e=`"${e}"`),e}t={...t,useSavedState:!1};let i=window.NL_ARGS.reduce((e,t,n)=>((t.includes("--path=")||t.includes("--debug-mode")||t.includes("--load-dir-res")||0==n)&&(e+=" "+o(t)),e),"");i+=" --url="+o(e);for(let e in t){if("processArgs"==e)continue;i+=` --window${"-"+e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}=${o(t[e])}`}t&&t.processArgs&&(i+=" "+t.processArgs),p(i,{background:!0}).then(e=>{n(e)}).catch(e=>{r(e)})})},exitFullScreen:function(){return l("window.exitFullScreen")},focus:function(){return l("window.focus")},getPosition:function(){return l("window.getPosition")},getSize:S,getTitle:function(){return l("window.getTitle")},hide:function(){return l("window.hide")},isFullScreen:function(){return l("window.isFullScreen")},isMaximized:function(){return l("window.isMaximized")},isMinimized:function(){return l("window.isMinimized")},isVisible:function(){return l("window.isVisible")},maximize:function(){return l("window.maximize")},minimize:function(){return l("window.minimize")},move:function(e,t){return l("window.move",{x:e,y:t})},print:function(){return l("window.print")},setAlwaysOnTop:function(e){return l("window.setAlwaysOnTop",{onTop:e})},setBorderless:function(e){return l("window.setBorderless",{borderless:e})},setDraggableRegion:function(e,t){return new Promise((n,r)=>{const o=e instanceof HTMLElement?e:document.getElementById(e);if(!o)return r({code:"NE_WD_DOMNOTF",message:"Unable to find DOM element"});if(b.has(o))return r({code:"NE_WD_ALRDREL",message:"This DOM element is already an active draggable region"});if(t?.exclude?.length){const e=new Set;for(const n of t.exclude){const t=n instanceof HTMLElement?n:document.getElementById(n);t&&e.add(t)}e.size&&D.set(o,e)}const a=(s=o,async function(e){if(0!==e.button)return;const t=D.get(s);if(t)for(const n of t)if(n.contains(e.target))return;await O(e.screenX,e.screenY),e.preventDefault()});var s;o.addEventListener("pointerdown",a),b.add(o),T.set(o,a);n({success:!0,message:"Draggable region was activated",exclusions:{add(...e){if(!b.has(o))throw{code:"NE_WD_NOTDRRE",message:"DOM element is no longer an active draggable region. You likely called unsetDraggableRegion on this element too early!"};let t=D.get(o);t||(t=new Set,D.set(o,t));const n=i(e);for(const e of n)t.add(e)},remove(...e){if(!b.has(o))throw{code:"NE_WD_NOTDRRE",message:"DOM element is no longer an active draggable region. You likely called unsetDraggableRegion on this element too early!"};const t=D.get(o);if(!t)return;const n=i(e);for(const e of n)t.delete(e)},removeAll(){if(!b.has(o))throw{code:"NE_WD_NOTDRRE",message:"DOM element is no longer an active draggable region. You likely called unsetDraggableRegion on this element too early!"};D.delete(o)}}})})},setFullScreen:function(){return l("window.setFullScreen")},setIcon:function(e){return l("window.setIcon",{icon:e})},setMainMenu:function(e){return l("window.setMainMenu",e)},setSize:function(e){return new Promise(async(t,n)=>{let r=await S();l("window.setSize",e={...r,...e}).then(e=>{t(e)}).catch(e=>{n(e)})})},setTitle:function(e){return l("window.setTitle",{title:e})},show:function(){return l("window.show")},snapshot:function(e){return l("window.snapshot",{path:e})},unmaximize:function(){return l("window.unmaximize")},unminimize:function(){return l("window.unminimize")},unsetDraggableRegion:function(e){return new Promise((t,n)=>{const r=e instanceof HTMLElement?e:document.getElementById(e);if(!r)return n({code:"NE_WD_DOMNOTF",message:"Unable to find DOM element"});if(!b.has(r))return n({code:"NE_WD_NOTDRRE",message:"DOM element is not an active draggable region"});const o=T.get(r);o&&(r.removeEventListener("pointerdown",o),T.delete(r)),b.delete(r),D.delete(r),t({success:!0,message:"Draggable region was deactivated"})})}};var M={__proto__:null,broadcast:function(e,t){return l("events.broadcast",{event:e,data:t})},dispatch:n,off:function(e,t){return window.removeEventListener(e,t),Promise.resolve({success:!0,message:"Event listener removed"})},on:t};function x(){return l("extensions.getStats")}var F={__proto__:null,broadcast:function(e,t){return l("extensions.broadcast",{event:e,data:t})},dispatch:function(e,t,n){return new Promise(async(r,o)=>{const i=await x();if(i.loaded.includes(e))if(i.connected.includes(e))try{r(await l("extensions.dispatch",{extensionId:e,event:t,data:n}))}catch(e){o(e)}else!function(e,t){e in u?u[e].push(t):u[e]=[t]}(e,{method:"extensions.dispatch",data:{extensionId:e,event:t,data:n},resolve:r,reject:o});else o({code:"NE_EX_EXTNOTL",message:`${e} is not loaded`})})},getStats:x};let R=null;var A={__proto__:null,checkForUpdates:function(e){return new Promise(async(t,n)=>{if(!e)return n({code:"NE_RT_NATRTER",message:"Missing require parameter: url"});try{const r=await fetch(e);R=JSON.parse(await r.text()),!function(e){return!!(e.applicationId&&e.applicationId==window.NL_APPID&&e.version&&e.resourcesURL)}(R)?n({code:"NE_UP_CUPDMER",message:"Invalid update manifest or mismatching applicationId"}):t(R)}catch(e){n({code:"NE_UP_CUPDERR",message:"Unable to fetch update manifest"})}})},install:function(){return new Promise(async(e,t)=>{if(!R)return t({code:"NE_UP_UPDNOUF",message:"No update manifest loaded. Make sure that updater.checkForUpdates() is called before install()."});try{const t=await fetch(R.resourcesURL),n=await t.arrayBuffer();await w(window.NL_PATH+"/resources.neu",n),e({success:!0,message:"Update installed. Restart the process to see updates"})}catch(e){t({code:"NE_UP_UPDINER",message:"Update installation error"})}})}};var I={__proto__:null,clear:function(){return l("clipboard.clear")},getFormat:function(){return l("clipboard.getFormat")},readHTML:function(){return l("clipboard.readHTML")},readImage:function(e=""){return new Promise((t,n)=>{l("clipboard.readImage").then(n=>{if(n){const r=window.atob(n.data);let o,i,a,s=32==n.bpp?4:3;switch(e.toLowerCase()){case"rgb":o=n.width*n.height*3,i=[0,1,2];break;case"rgba":o=n.width*n.height*4,i=[0,1,2,3];break;case"argb":o=n.width*n.height*4,i=[3,0,1,2];break;case"bgra":o=n.width*n.height*4,i=[2,1,0,3];break;default:o=r.length,a=new Uint8Array(o);for(let e=0;e>>0:(c<<24|u<<16|d<<8|l)>>>0,w=[f>>n.redShift&255,f>>n.greenShift&255,f>>n.blueShift&255,f>>n.alphaShift&255],i.forEach((e,t)=>{a[t+m]=w[e]}),m+=i.length;n.data=a}t(n)}).catch(e=>{n(e)})})},readText:function(){return l("clipboard.readText")},writeHTML:function(e){return l("clipboard.writeHTML",{data:e})},writeImage:function(e){const t={...e};return e?.data&&(t.data=o(e.data)),l("clipboard.writeImage",t)},writeText:function(e){return l("clipboard.writeText",{data:e})}};var C={__proto__:null,extractDirectory:function(e,t){return l("resources.extractDirectory",{path:e,destination:t})},extractFile:function(e,t){return l("resources.extractFile",{path:e,destination:t})},getFiles:function(){return l("resources.getFiles")},getStats:function(e){return l("resources.getStats",{path:e})},readBinaryFile:function(e){return new Promise((t,n)=>{l("resources.readBinaryFile",{path:e}).then(e=>{t(r(e))}).catch(e=>{n(e)})})},readFile:function(e){return l("resources.readFile",{path:e})}};var U={__proto__:null,getMounts:function(){return l("server.getMounts")},mount:function(e,t){return l("server.mount",{path:e,target:t})},unmount:function(e){return l("server.unmount",{path:e})}};var k={__proto__:null,getMethods:function(){return l("custom.getMethods")}};let z=!1;return e.app=P,e.clipboard=I,e.computer=y,e.custom=k,e.debug=N,e.events=M,e.extensions=F,e.filesystem=m,e.init=function(e={}){if(e={exportCustomMethods:!0,...e},!z){if(d(),window.NL_ARGS.find(e=>"--neu-dev-auto-reload"==e)&&t("neuDev_reloadApp",async()=>{await v("Reloading the application..."),location.reload()}),e.exportCustomMethods&&window.NL_CMETHODS&&window.NL_CMETHODS.length>0)for(const e of window.NL_CMETHODS)Neutralino.custom[e]=(...t)=>{let n={};for(const[e,r]of t.entries())n="object"!=typeof r||Array.isArray(r)||null==r?{...n,["arg"+e]:r}:{...n,...r};return l("custom."+e,n)};window.NL_CVERSION="6.5.0",window.NL_CCOMMIT="425c526c318342e0e5d0f17caceef2a53049eda4",z=!0}},e.os=h,e.resources=C,e.server=U,e.storage=_,e.updater=A,e.window=L,e}({}); diff --git a/public/neutralino_loader.html b/public/neutralino_loader.html new file mode 100644 index 0000000..70c3736 --- /dev/null +++ b/public/neutralino_loader.html @@ -0,0 +1,147 @@ + + + + + + Monochrome Shell + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 827ce43..e71e719 100644 --- a/vite.config.js +++ b/vite.config.js @@ -9,8 +9,8 @@ export default defineConfig(({ mode }) => { return { base: './', build: { - outDir: IS_NEUTRALINO ? 'www' : 'dist', - emptyOutDir: IS_NEUTRALINO, + outDir: 'dist', + emptyOutDir: true, }, plugins: [ IS_NEUTRALINO && neutralino(),