refactor(desktop): separate js for neutralino from the js used on the website

This commit is contained in:
Julien Maille 2026-02-12 14:27:27 +01:00
parent 0213132606
commit cafa97cb0f
6 changed files with 79 additions and 78 deletions

View file

@ -4561,7 +4561,6 @@
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/pocketbase@0.21.3/dist/pocketbase.umd.js"></script>
<script src="/neutralino.js"></script>
<script type="module" src="/js/app.js"></script>
</body>
</html>

View file

@ -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);

22
js/desktop/desktop.js Normal file
View file

@ -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);
}
}

View file

@ -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';

View file

@ -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 }, '*');
},
};

View file

@ -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.*"]
}