mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-06-27 10:11:55 +08:00
feat: Add App Center plugin functionality
This commit is contained in:
120
web/src/plugins/runtime.ts
Normal file
120
web/src/plugins/runtime.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import { pluginsApi, type PluginManifestUpdate } from '../api/plugins';
|
||||
|
||||
export interface RegisteredPlugin {
|
||||
mount: (container: HTMLElement, ctx: {
|
||||
filePath: string;
|
||||
entry: any;
|
||||
urls: { downloadUrl: string };
|
||||
host: HostApi;
|
||||
}) => void | Promise<void>;
|
||||
unmount?: (container: HTMLElement) => void | Promise<void>;
|
||||
|
||||
key?: string;
|
||||
name?: string;
|
||||
version?: string;
|
||||
supportedExts?: string[];
|
||||
defaultBounds?: { x?: number; y?: number; width?: number; height?: number };
|
||||
defaultMaximized?: boolean;
|
||||
icon?: string;
|
||||
description?: string;
|
||||
author?: string;
|
||||
website?: string;
|
||||
github?: string;
|
||||
}
|
||||
|
||||
export interface HostApi {
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
const loadedPlugins = new Map<string, RegisteredPlugin>();
|
||||
const waiters = new Map<string, ((p: RegisteredPlugin) => void)[]>();
|
||||
const injected = new Set<string>();
|
||||
|
||||
declare global {
|
||||
interface Window { FoxelRegister?: (plugin: RegisteredPlugin) => void; }
|
||||
}
|
||||
|
||||
window.FoxelRegister = (plugin: RegisteredPlugin) => {
|
||||
const pendingUrl = sessionStorage.getItem('foxel:pendingPluginUrl') || '';
|
||||
if (pendingUrl) {
|
||||
loadedPlugins.set(pendingUrl, plugin);
|
||||
const resolvers = waiters.get(pendingUrl) || [];
|
||||
resolvers.forEach(fn => fn(plugin));
|
||||
waiters.delete(pendingUrl);
|
||||
sessionStorage.removeItem('foxel:pendingPluginUrl');
|
||||
} else {
|
||||
const anyUrl = Array.from(waiters.keys())[0];
|
||||
if (anyUrl) {
|
||||
loadedPlugins.set(anyUrl, plugin);
|
||||
const resolvers = waiters.get(anyUrl) || [];
|
||||
resolvers.forEach(fn => fn(plugin));
|
||||
waiters.delete(anyUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export async function loadPluginFromUrl(url: string): Promise<RegisteredPlugin> {
|
||||
const existing = loadedPlugins.get(url);
|
||||
if (existing) return existing;
|
||||
return new Promise<RegisteredPlugin>((resolve, reject) => {
|
||||
const arr = waiters.get(url) || [];
|
||||
arr.push(resolve);
|
||||
waiters.set(url, arr);
|
||||
|
||||
const ready = loadedPlugins.get(url);
|
||||
if (ready) {
|
||||
const resolvers = waiters.get(url) || [];
|
||||
resolvers.forEach(fn => fn(ready));
|
||||
waiters.delete(url);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.setItem('foxel:pendingPluginUrl', url);
|
||||
|
||||
if (!injected.has(url)) {
|
||||
injected.add(url);
|
||||
const script = document.createElement('script');
|
||||
script.src = url;
|
||||
script.async = true;
|
||||
script.onerror = () => {
|
||||
waiters.delete(url);
|
||||
reject(new Error('Failed to load plugin script: ' + url));
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
const t = setTimeout(() => {
|
||||
if (!loadedPlugins.get(url)) {
|
||||
waiters.delete(url);
|
||||
reject(new Error('Plugin did not call FoxelRegister: ' + url));
|
||||
}
|
||||
}, 15000);
|
||||
|
||||
const last = arr[arr.length - 1];
|
||||
arr[arr.length - 1] = (p: RegisteredPlugin) => { clearTimeout(t); last(p); };
|
||||
});
|
||||
}
|
||||
|
||||
export async function ensureManifest(pluginId: number, plugin: RegisteredPlugin) {
|
||||
const manifest: PluginManifestUpdate = {
|
||||
key: plugin.key,
|
||||
name: plugin.name,
|
||||
version: plugin.version,
|
||||
supported_exts: plugin.supportedExts,
|
||||
default_bounds: plugin.defaultBounds,
|
||||
default_maximized: plugin.defaultMaximized,
|
||||
icon: plugin.icon,
|
||||
description: plugin.description,
|
||||
author: plugin.author,
|
||||
website: plugin.website,
|
||||
github: plugin.github,
|
||||
};
|
||||
try { console.debug('[foxel] report manifest', pluginId, manifest); } catch { }
|
||||
const key = `foxel:manifestReported:${pluginId}`;
|
||||
if (sessionStorage.getItem(key) === '1') return;
|
||||
try {
|
||||
await pluginsApi.updateManifest(pluginId, manifest);
|
||||
sessionStorage.setItem(key, '1');
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user