kv-music/public/neutralino_loader.html
2026-02-12 15:00:39 +00:00

158 lines
6.2 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Monochrome Shell</title>
<style>
body,
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
overflow: hidden;
background-color: #000;
/* Seamless blend */
}
iframe {
width: 100%;
height: 100%;
border: none;
display: block;
}
</style>
</head>
<body>
<script src="__neutralino_globals.js"></script>
<script src="neutralino.js"></script>
<!-- Load the app from the local Neutralino server -->
<iframe id="app-frame" allow="autoplay; fullscreen; microphone; clipboard-read; clipboard-write"></iframe>
<script>
// initialize Neutralino in the Shell (Local Context)
try {
Neutralino.init();
console.log('[Shell] Neutralino initialized.');
} catch (e) {
console.error('[Shell] Failed to init Neutralino:', e);
}
// Point iframe to local server using the port from Neutralino
// NL_PORT is available globally after init (or we can parse it/wait for it)
// Neutralino.init() usually populates window.NL_PORT or we read it from sessionStorage
const initFrame = async () => {
// Simplified Dev Mode Detection using Neutralino's internal args
// 'neu run' adds --neu-dev-auto-reload, which we can use to detect dev environment.
const args = window.NL_ARGS || [];
const isDev = args.some((arg) => arg.includes('--neu-dev-auto-reload') || arg.includes('--debug-mode'));
// Static Dev Port
const DEV_PORT = '5173';
let port = window.NL_PORT || sessionStorage.getItem('NL_PORT') || '5050';
const iframe = document.getElementById('app-frame');
const targetPort = isDev ? DEV_PORT : port;
const targetUrl = `http://localhost:${targetPort}/?mode=neutralino`;
if (isDev) {
console.log(`[Shell] Dev mode detected via NL_ARGS. Waiting 2s for Vite on port ${targetPort}...`);
await new Promise((r) => setTimeout(r, 2000));
} else {
console.log(`[Shell] Production mode detected.`);
}
console.log(`[Shell] Loading app from: ${targetUrl}`);
iframe.src = targetUrl;
};
initFrame();
const iframe = document.getElementById('app-frame');
// Forward generic Neutralino events to the Iframe
const forwardEvent = (eventName, detail) => {
if (iframe && iframe.contentWindow) {
iframe.contentWindow.postMessage(
{
type: 'NL_EVENT',
eventName: eventName,
detail: detail,
},
'*'
);
}
};
// Listen for specific events to forward
// Add more here if the app needs them (e.g., tray events)
Neutralino.events.on('windowClose', () => forwardEvent('windowClose'));
Neutralino.events.on('windowFocus', () => forwardEvent('windowFocus'));
Neutralino.events.on('windowBlur', () => forwardEvent('windowBlur'));
// Handle commands from the Iframe (via Bridge)
window.addEventListener('message', async (event) => {
const { type, eventName, data, extensionId } = event.data;
// Security: In a real scenario, check event.origin if possible.
// But since this loads valid HTTPS content, it's generally safe for this context.
switch (type) {
case 'NL_INIT':
console.log('[Shell] Bridge connected.');
break;
case 'NL_BROADCAST':
// e.g. Discord RPC updates
try {
// console.log('[Shell] Broadcasting:', eventName, data);
await Neutralino.events.broadcast(eventName, data);
} catch (e) {
console.error('[Shell] Broadcast failed:', e);
}
break;
case 'NL_EXTENSION':
// e.g. specific extension dispatch
try {
// console.log('[Shell] Dispatching to extension:', extensionId, eventName);
await Neutralino.extensions.dispatch(extensionId, eventName, data);
} catch (e) {
console.error('[Shell] Extension dispatch failed:', e);
}
break;
case 'NL_APP_EXIT':
Neutralino.app.exit();
break;
case 'NL_WINDOW_MIN':
Neutralino.window.minimize();
break;
case 'NL_WINDOW_MAX':
try {
const isMax = await Neutralino.window.isMaximized();
if (isMax) Neutralino.window.unmaximize();
else Neutralino.window.maximize();
} catch (e) {
console.error('[Shell] Window toggle failed:', e);
}
break;
case 'NL_WINDOW_SET_TITLE':
try {
await Neutralino.window.setTitle(event.data.title);
} catch (e) {
console.error('[Shell] Set title failed:', e);
}
break;
}
});
</script>
</body>
</html>