Files
meridian-os/js/core/registry.js

149 lines
3.8 KiB
JavaScript

import MeridianWM from './wm.js';
import State from './state.js';
import Bus from './bus.js';
const Registry = (() => {
const APPS = {};
let _launcherOpen = false;
function registerApp(def) {
if (def.id) {
APPS[def.id] = def;
}
}
function unregisterApp(id) {
delete APPS[id];
}
function getApp(id) {
return APPS[id] || null;
}
function launchApp(id) {
const def = APPS[id];
if (!def) {
console.warn(`App ${id} not registered`);
return;
}
const title = def.title || id;
const w = def.width || 640;
const h = def.height || 480;
// Create window with a render-callback; wm will mount container then call it.
const win = MeridianWM.openWindow({
app: id,
title,
w, h,
content: (container) => {
// Tag container for per-app CSS scoping
container.setAttribute('data-app', id);
const api = {
state: State,
bus: Bus,
close: () => MeridianWM.closeWindow(win && win.id),
setTitle: (t) => {
const el = document.getElementById(win && win.id);
if (el) {
const titleEl = el.querySelector('.m-window-title');
if (titleEl) titleEl.textContent = t;
}
},
};
try {
def.render(container, api);
} catch (e) {
container.textContent = `Error loading ${id}: ${e.message}`;
console.error(`[${id}] render error`, e);
}
},
});
closeLauncher();
}
function getRegisteredApps() {
return Object.values(APPS);
}
function toggleLauncher() {
_launcherOpen = !_launcherOpen;
const popover = document.getElementById('launcher-popover');
const btn = document.getElementById('launcher');
if (popover) {
if (_launcherOpen) {
renderLauncherApps(popover);
popover.classList.remove('hidden');
} else {
popover.classList.add('hidden');
}
}
if (btn) {
btn.classList.toggle('active', _launcherOpen);
}
return _launcherOpen;
}
function closeLauncher() {
_launcherOpen = false;
const popover = document.getElementById('launcher-popover');
const btn = document.getElementById('launcher');
if (popover) popover.classList.add('hidden');
if (btn) btn.classList.remove('active');
}
function renderLauncherApps(popover) {
const apps = getRegisteredApps();
popover.innerHTML = '';
if (apps.length === 0) {
popover.innerHTML = '<div style="padding:12px;text-align:center;color:var(--text-faint);font-size:13px;">No apps registered</div>';
return;
}
for (const app of apps) {
const btn = document.createElement('button');
btn.className = 'launcher-app-btn';
btn.setAttribute('aria-label', `Launch ${app.title || app.id}`);
btn.innerHTML = `
<div class="launcher-app-icon">${app.icon || '&#9670;'}</div>
<div class="launcher-app-label">${escapeHtml(app.title || app.id)}</div>
`;
btn.addEventListener('click', () => launchApp(app.id));
popover.appendChild(btn);
}
}
function escapeHtml(str) {
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
function init() {
const btn = document.getElementById('launcher');
if (btn) {
btn.addEventListener('click', toggleLauncher);
}
// Close launcher when clicking elsewhere
document.addEventListener('mousedown', (e) => {
if (_launcherOpen && !e.target.closest('#launcher') && !e.target.closest('#launcher-popover')) {
closeLauncher();
}
});
}
return {
APPS,
registerApp,
unregisterApp,
getApp,
launchApp,
getRegisteredApps,
toggleLauncher,
init
};
})();
export default Registry;
export { Registry };