mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 15:06:20 +00:00
194 lines
6.0 KiB
TypeScript
194 lines
6.0 KiB
TypeScript
import { GetRoomEngine } from '@nitrots/nitro-renderer';
|
|
import { CreateLinkEvent, GetRoomSession, SendMessageComposer } from '../../api';
|
|
|
|
/**
|
|
* Plugin descriptor registered by external plugin scripts.
|
|
*/
|
|
export interface INitroPlugin
|
|
{
|
|
/** Unique plugin name */
|
|
name: string;
|
|
/** Label shown on the button in room tools */
|
|
label: string;
|
|
/** CSS class for the icon (nitro-icon class) */
|
|
icon?: string;
|
|
/** Called when the plugin button is clicked */
|
|
onOpen: () => void;
|
|
/** Called to close/destroy the plugin UI */
|
|
onClose?: () => void;
|
|
/** Called when the plugin is first loaded, receives the Nitro API */
|
|
onInit?: (api: INitroPluginApi) => void;
|
|
}
|
|
|
|
/**
|
|
* API exposed to external plugins via window.NitroPlugins
|
|
*/
|
|
export interface INitroPluginApi
|
|
{
|
|
/** Register a plugin */
|
|
register: (plugin: INitroPlugin) => void;
|
|
/** Unregister a plugin by name */
|
|
unregister: (name: string) => void;
|
|
/** Get all registered plugins */
|
|
getPlugins: () => INitroPlugin[];
|
|
/** Fire a Nitro link event (e.g., 'navigator/toggle-room-info') */
|
|
createLinkEvent: (link: string) => void;
|
|
/** Get the room engine instance */
|
|
getRoomEngine: () => ReturnType<typeof GetRoomEngine>;
|
|
/** Get the current room session */
|
|
getRoomSession: () => ReturnType<typeof GetRoomSession>;
|
|
/** Send a message composer to the server */
|
|
sendMessage: typeof SendMessageComposer;
|
|
/** Create a draggable floating window and return its container element */
|
|
createWindow: (id: string, title: string, width: number) => HTMLDivElement | null;
|
|
/** Destroy a floating window by id */
|
|
destroyWindow: (id: string) => void;
|
|
}
|
|
|
|
// Internal plugin storage
|
|
const _plugins: INitroPlugin[] = [];
|
|
const _listeners: Array<() => void> = [];
|
|
|
|
function notifyListeners()
|
|
{
|
|
_listeners.forEach(fn => fn());
|
|
}
|
|
|
|
const pluginApi: INitroPluginApi = {
|
|
register(plugin: INitroPlugin)
|
|
{
|
|
if (_plugins.some(p => p.name === plugin.name)) return;
|
|
_plugins.push(plugin);
|
|
plugin.onInit?.(pluginApi);
|
|
notifyListeners();
|
|
},
|
|
|
|
unregister(name: string)
|
|
{
|
|
const index = _plugins.findIndex(p => p.name === name);
|
|
if (index >= 0)
|
|
{
|
|
_plugins[index].onClose?.();
|
|
_plugins.splice(index, 1);
|
|
notifyListeners();
|
|
}
|
|
},
|
|
|
|
getPlugins()
|
|
{
|
|
return [..._plugins];
|
|
},
|
|
|
|
createLinkEvent(link: string)
|
|
{
|
|
CreateLinkEvent(link);
|
|
},
|
|
|
|
getRoomEngine()
|
|
{
|
|
return GetRoomEngine();
|
|
},
|
|
|
|
getRoomSession()
|
|
{
|
|
return GetRoomSession();
|
|
},
|
|
|
|
sendMessage: SendMessageComposer,
|
|
|
|
createWindow(id: string, title: string, width: number): HTMLDivElement | null
|
|
{
|
|
// Remove existing window with same id
|
|
pluginApi.destroyWindow(id);
|
|
|
|
// Create overlay container
|
|
const overlay = document.createElement('div');
|
|
overlay.id = `nitro-plugin-window-${id}`;
|
|
overlay.style.cssText = `position:fixed;z-index:500;top:50%;left:50%;transform:translate(-50%,-50%)`;
|
|
|
|
// Card wrapper
|
|
const card = document.createElement('div');
|
|
card.style.cssText = `width:${width}px;background:#2c3e50;border:1px solid #283F5D;border-radius:6px;box-shadow:0 8px 32px rgba(0,0,0,0.5);overflow:hidden;font-family:Ubuntu,sans-serif`;
|
|
|
|
// Header (draggable)
|
|
const header = document.createElement('div');
|
|
header.style.cssText = `display:flex;align-items:center;justify-content:center;position:relative;min-height:33px;background:linear-gradient(180deg,#3c6a8e 0%,#2a4f6e 100%);cursor:move;user-select:none`;
|
|
|
|
const titleEl = document.createElement('span');
|
|
titleEl.textContent = title;
|
|
titleEl.style.cssText = `color:#fff;font-size:16px;text-shadow:0 1px 2px rgba(0,0,0,0.5)`;
|
|
|
|
const closeBtn = document.createElement('div');
|
|
closeBtn.style.cssText = `position:absolute;right:8px;width:20px;height:20px;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:14px;border-radius:50%;background:rgba(255,255,255,0.1)`;
|
|
closeBtn.innerHTML = '✕';
|
|
closeBtn.addEventListener('click', () => pluginApi.destroyWindow(id));
|
|
|
|
header.appendChild(titleEl);
|
|
header.appendChild(closeBtn);
|
|
|
|
// Make draggable
|
|
let isDragging = false;
|
|
let offsetX = 0, offsetY = 0;
|
|
|
|
header.addEventListener('mousedown', (e: MouseEvent) =>
|
|
{
|
|
isDragging = true;
|
|
const rect = overlay.getBoundingClientRect();
|
|
offsetX = e.clientX - rect.left;
|
|
offsetY = e.clientY - rect.top;
|
|
overlay.style.transform = 'none';
|
|
overlay.style.left = rect.left + 'px';
|
|
overlay.style.top = rect.top + 'px';
|
|
});
|
|
|
|
document.addEventListener('mousemove', (e: MouseEvent) =>
|
|
{
|
|
if (!isDragging) return;
|
|
overlay.style.left = (e.clientX - offsetX) + 'px';
|
|
overlay.style.top = (e.clientY - offsetY) + 'px';
|
|
});
|
|
|
|
document.addEventListener('mouseup', () => { isDragging = false; });
|
|
|
|
// Content area
|
|
const content = document.createElement('div');
|
|
content.style.cssText = `padding:16px`;
|
|
|
|
card.appendChild(header);
|
|
card.appendChild(content);
|
|
overlay.appendChild(card);
|
|
document.body.appendChild(overlay);
|
|
|
|
return content;
|
|
},
|
|
|
|
destroyWindow(id: string)
|
|
{
|
|
const existing = document.getElementById(`nitro-plugin-window-${id}`);
|
|
if (existing) existing.remove();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Subscribe to plugin list changes. Returns unsubscribe function.
|
|
*/
|
|
export function subscribePlugins(listener: () => void): () => void
|
|
{
|
|
_listeners.push(listener);
|
|
return () =>
|
|
{
|
|
const idx = _listeners.indexOf(listener);
|
|
if (idx >= 0) _listeners.splice(idx, 1);
|
|
};
|
|
}
|
|
|
|
export function getRegisteredPlugins(): INitroPlugin[]
|
|
{
|
|
return [..._plugins];
|
|
}
|
|
|
|
// Expose globally so external scripts can use it
|
|
(window as any).NitroPlugins = pluginApi;
|
|
|
|
export { pluginApi };
|