From cafa97cb0f1ec06c4b6d1088c4165ef3a1f8cd43 Mon Sep 17 00:00:00 2001 From: Julien Maille Date: Thu, 12 Feb 2026 14:27:27 +0100 Subject: [PATCH] refactor(desktop): separate js for neutralino from the js used on the website --- index.html | 1 - js/app.js | 68 +++++---------------------- js/desktop/desktop.js | 22 +++++++++ js/{ => desktop}/discord-rpc.js | 3 +- js/{ => desktop}/neutralino-bridge.js | 55 ++++++++++++++++------ neutralino.config.json | 8 ++-- 6 files changed, 79 insertions(+), 78 deletions(-) create mode 100644 js/desktop/desktop.js rename js/{ => desktop}/discord-rpc.js (97%) rename js/{ => desktop}/neutralino-bridge.js (55%) diff --git a/index.html b/index.html index 096b2b9..0c27310 100644 --- a/index.html +++ b/index.html @@ -4561,7 +4561,6 @@ - diff --git a/js/app.js b/js/app.js index 5679124..1cfd526 100644 --- a/js/app.js +++ b/js/app.js @@ -21,15 +21,8 @@ import { sidePanelManager } from './side-panel.js'; 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 './smooth-scrolling.js'; -// Assign Neutralino to window for global access -if (typeof window !== 'undefined' && window.NL_MODE) { - window.Neutralino = Neutralino; -} - import { initTracker } from './tracker.js'; // Lazy-loaded modules @@ -238,46 +231,7 @@ async function disablePwaForAuthGate() { } document.addEventListener('DOMContentLoaded', async () => { - // Initialize desktop environment (Neutralino) - const urlParams = new URLSearchParams(window.location.search); - const hasNLParams = urlParams.has('NL_PORT') || urlParams.has('NL_TOKEN'); - const isDesktop = - typeof window !== 'undefined' && (window.NL_MODE || window.location.port === '5050' || hasNLParams); - - if (typeof window !== 'undefined') { - const nlGlobals = Object.keys(window).filter((k) => k.startsWith('NL_')); - console.log('[App] Environment Check:', { - isDesktop, - port: window.location.port, - hasNLParams, - nlGlobals, - }); - } - - if (typeof window !== 'undefined' && window.Neutralino) { - if (isDesktop) { - console.log('[App] Initializing Neutralino desktop environment...'); - try { - Neutralino.init(); - console.log('[App] Neutralino.init() called successfully.'); - - // 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); - } - } else { - console.log('[App] Skipping Neutralino.init() on regular web environment.'); - } - } else { - console.log('[App] Neutralino object NOT detected.'); - } - const api = new MusicAPI(apiSettings); - const audioPlayer = document.getElementById('audio-player'); // i love ios and macos!!!! webkit fucking SUCKS BULLSHIT sorry ios/macos heads yall getting lossless only @@ -308,6 +262,17 @@ document.addEventListener('DOMContentLoaded', async () => { const currentQuality = localStorage.getItem('playback-quality') || 'HI_RES_LOSSLESS'; const player = new Player(audioPlayer, api, currentQuality); + // Initialize tracker + initTracker(player); + + // Initialize desktop features if in Neutralino mode + if (typeof window !== 'undefined' && (window.NL_MODE || window.location.search.includes('mode=neutralino'))) { + import('./desktop/desktop.js').then((m) => m.initDesktop(player)); + } + + const castBtn = document.getElementById('cast-btn'); + initializeCasting(audioPlayer, castBtn); + const ui = new UIRenderer(api, player); const scrobbler = new MultiScrobbler(); const lyricsManager = new LyricsManager(api); @@ -421,17 +386,6 @@ document.addEventListener('DOMContentLoaded', async () => { initializeUIInteractions(player, api, ui); initializeKeyboardShortcuts(player, audioPlayer); - // Initialize tracker - initTracker(player); - - if (typeof window !== 'undefined' && window.Neutralino && isDesktop) { - console.log('[App] Starting Discord RPC...'); - initializeDiscordRPC(player); - } - - const castBtn = document.getElementById('cast-btn'); - initializeCasting(audioPlayer, castBtn); - // Restore UI state for the current track (like button, theme) if (player.currentTrack) { ui.setCurrentTrack(player.currentTrack); diff --git a/js/desktop/desktop.js b/js/desktop/desktop.js new file mode 100644 index 0000000..36f9ca6 --- /dev/null +++ b/js/desktop/desktop.js @@ -0,0 +1,22 @@ +// js/desktop/desktop.js +import Neutralino from './neutralino-bridge.js'; +import { initializeDiscordRPC } from './discord-rpc.js'; + +export async function initDesktop(player) { + console.log('[Desktop] Initializing desktop features...'); + + // Assign to window for modules that use global Neutralino (like Player.js) + window.Neutralino = Neutralino; + + try { + await Neutralino.init(); + console.log('[Desktop] Neutralino initialized.'); + + if (player) { + console.log('[Desktop] Starting Discord RPC...'); + initializeDiscordRPC(player); + } + } catch (error) { + console.error('[Desktop] Failed to initialize desktop environment:', error); + } +} diff --git a/js/discord-rpc.js b/js/desktop/discord-rpc.js similarity index 97% rename from js/discord-rpc.js rename to js/desktop/discord-rpc.js index e3983cf..61c3be1 100644 --- a/js/discord-rpc.js +++ b/js/desktop/discord-rpc.js @@ -1,4 +1,5 @@ -import { getTrackTitle, getTrackArtists } from './utils.js'; +// js/desktop/discord-rpc.js +import { getTrackTitle, getTrackArtists } from '../utils.js'; export function initializeDiscordRPC(player) { const EXTENSION_ID = 'js.neutralino.discordrpc'; diff --git a/js/neutralino-bridge.js b/js/desktop/neutralino-bridge.js similarity index 55% rename from js/neutralino-bridge.js rename to js/desktop/neutralino-bridge.js index 5682e9c..dddb86f 100644 --- a/js/neutralino-bridge.js +++ b/js/desktop/neutralino-bridge.js @@ -1,69 +1,94 @@ -// js/neutralino-bridge.js +// js/desktop/neutralino-bridge.js + +const isNeutralino = typeof window !== 'undefined' && ( + window.NL_MODE || + window.location.search.includes('mode=neutralino') || + (window.parent !== window) +); 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); - } - }); +if (isNeutralino) { + 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 () => { + if (!isNeutralino) return; // Notify Shell we are ready window.parent.postMessage({ type: 'NL_INIT' }, '*'); }; export const events = { on: (eventName, handler) => { + if (!isNeutralino) return; if (!listeners.has(eventName)) { listeners.set(eventName, []); } listeners.get(eventName).push(handler); }, off: (eventName, handler) => { + if (!isNeutralino) return; 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) => { + if (!isNeutralino) return; window.parent.postMessage({ type: 'NL_BROADCAST', eventName, data }, '*'); }, }; export const extensions = { dispatch: async (extensionId, eventName, data) => { + if (!isNeutralino) return; window.parent.postMessage({ type: 'NL_EXTENSION', extensionId, eventName, data }, '*'); }, }; export const app = { exit: async () => { + if (!isNeutralino) return; window.parent.postMessage({ type: 'NL_APP_EXIT' }, '*'); }, }; -const _window = { +export const _window = { minimize: async () => { + if (!isNeutralino) return; window.parent.postMessage({ type: 'NL_WINDOW_MIN' }, '*'); }, maximize: async () => { + if (!isNeutralino) return; window.parent.postMessage({ type: 'NL_WINDOW_MAX' }, '*'); }, + show: async () => { + if (!isNeutralino) return; + window.parent.postMessage({ type: 'NL_WINDOW_SHOW' }, '*'); + }, + hide: async () => { + if (!isNeutralino) return; + window.parent.postMessage({ type: 'NL_WINDOW_HIDE' }, '*'); + }, isVisible: async () => { return true; // Mock response }, setTitle: async (title) => { + if (!isNeutralino) return; window.parent.postMessage({ type: 'NL_WINDOW_SET_TITLE', title }, '*'); }, }; diff --git a/neutralino.config.json b/neutralino.config.json index 798e0fa..078e74c 100644 --- a/neutralino.config.json +++ b/neutralino.config.json @@ -1,9 +1,9 @@ { "applicationId": "com.monochrome.app", "applicationName": "Monochrome", - "applicationIcon": "public/assets/512.png", + "applicationIcon": "public/assets/appicon.png", "author": "Monochrome", - "description": "Lossless music streaming", + "description": "Monochrome - Lossless music streaming", "version": "1.0.0", "defaultMode": "window", "documentRoot": "dist/", @@ -15,7 +15,7 @@ "modes": { "window": { "title": "Monochrome", - "icon": "public/assets/512.png", + "icon": "public/assets/appicon.png", "width": 1280, "height": 800, "minWidth": 800, @@ -44,5 +44,5 @@ "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.*", "os.*"] }