- apps/captain-mobile: Mobile API service - apps/flow-ui: Flow UI application - apps/mindlink: Mindlink application - apps/storage: Storage API and workers - apps/tzzr-cli: TZZR CLI tool - deck-frontend/backups: Historical TypeScript versions - hst-frontend: Standalone HST frontend Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
175 lines
4.5 KiB
TypeScript
175 lines
4.5 KiB
TypeScript
/**
|
|
* ModuleLoader - Carga dinámica de módulos
|
|
*
|
|
* Responsabilidades:
|
|
* - Cargar el módulo correcto según la base
|
|
* - Manejar cache de módulos
|
|
* - Gestionar lifecycle (mount/unmount)
|
|
*/
|
|
|
|
import { BaseModule, type ModuleContext } from './registry.ts';
|
|
import { getModuleConfig, isModuleEnabled } from './configs/index.ts';
|
|
import { StandardModule } from './standard/index.ts';
|
|
import type { Store } from '@/state/store.ts';
|
|
import type { AppState, BaseType } from '@/types/index.ts';
|
|
|
|
export interface LoaderTargets {
|
|
container: HTMLElement;
|
|
leftPanel: HTMLElement;
|
|
groupsBar: HTMLElement;
|
|
showDetail: (mrf: string) => void;
|
|
}
|
|
|
|
export class ModuleLoader {
|
|
private store: Store<AppState>;
|
|
private currentModule: BaseModule | null = null;
|
|
private currentBase: BaseType | null = null;
|
|
|
|
constructor(store: Store<AppState>) {
|
|
this.store = store;
|
|
}
|
|
|
|
/**
|
|
* Cargar módulo para una base
|
|
*/
|
|
async load(base: BaseType, targets: LoaderTargets): Promise<void> {
|
|
const config = getModuleConfig(base);
|
|
|
|
// Verificar si el módulo está habilitado
|
|
if (!isModuleEnabled(base)) {
|
|
this.showDisabledMessage(targets.container, config.name);
|
|
targets.leftPanel.innerHTML = '';
|
|
targets.groupsBar.innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
// Si ya está cargado el mismo módulo, solo re-renderizar
|
|
if (this.currentBase === base && this.currentModule) {
|
|
this.currentModule.render();
|
|
return;
|
|
}
|
|
|
|
// Unmount módulo actual
|
|
this.currentModule?.unmount();
|
|
this.currentModule = null;
|
|
this.currentBase = null;
|
|
|
|
// Show loading
|
|
targets.container.innerHTML = '<div class="loading">Cargando...</div>';
|
|
|
|
// Crear contexto
|
|
const ctx: ModuleContext = {
|
|
container: targets.container,
|
|
leftPanel: targets.leftPanel,
|
|
groupsBar: targets.groupsBar,
|
|
store: this.store,
|
|
config,
|
|
showDetail: targets.showDetail
|
|
};
|
|
|
|
// Crear módulo según tipo
|
|
let module: BaseModule;
|
|
|
|
switch (config.renderType) {
|
|
case 'standard':
|
|
module = new StandardModule(ctx);
|
|
break;
|
|
|
|
case 'chat':
|
|
case 'custom':
|
|
// Carga dinámica de módulos custom
|
|
if (config.customModule) {
|
|
try {
|
|
const { default: CustomModule } = await config.customModule();
|
|
module = new CustomModule(ctx);
|
|
} catch (error) {
|
|
console.error(`Failed to load custom module for ${base}:`, error);
|
|
this.showErrorMessage(targets.container, `Error cargando módulo ${config.name}`);
|
|
return;
|
|
}
|
|
} else {
|
|
this.showDisabledMessage(targets.container, config.name);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
console.error(`Unknown render type: ${config.renderType}`);
|
|
this.showErrorMessage(targets.container, 'Tipo de módulo desconocido');
|
|
return;
|
|
}
|
|
|
|
// Mount módulo
|
|
try {
|
|
await module.mount();
|
|
this.currentModule = module;
|
|
this.currentBase = base;
|
|
} catch (error) {
|
|
console.error(`Failed to mount module for ${base}:`, error);
|
|
this.showErrorMessage(targets.container, `Error inicializando ${config.name}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Re-renderizar módulo actual (ej: cuando cambia la vista)
|
|
*/
|
|
rerender(): void {
|
|
if (this.currentModule) {
|
|
this.currentModule.render();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Re-renderizar sidebar del módulo actual
|
|
*/
|
|
rerenderSidebar(): void {
|
|
if (this.currentModule) {
|
|
this.currentModule.renderSidebar();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Obtener módulo actual
|
|
*/
|
|
getCurrentModule(): BaseModule | null {
|
|
return this.currentModule;
|
|
}
|
|
|
|
/**
|
|
* Obtener base actual
|
|
*/
|
|
getCurrentBase(): BaseType | null {
|
|
return this.currentBase;
|
|
}
|
|
|
|
/**
|
|
* Unmount módulo actual
|
|
*/
|
|
unmount(): void {
|
|
this.currentModule?.unmount();
|
|
this.currentModule = null;
|
|
this.currentBase = null;
|
|
}
|
|
|
|
private showDisabledMessage(container: HTMLElement, moduleName: string): void {
|
|
container.innerHTML = `
|
|
<div class="module-disabled">
|
|
<div class="module-disabled-icon">🚧</div>
|
|
<div class="module-disabled-title">${moduleName}</div>
|
|
<div class="module-disabled-text">Próximamente</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
private showErrorMessage(container: HTMLElement, message: string): void {
|
|
container.innerHTML = `
|
|
<div class="module-error">
|
|
<div class="module-error-icon">⚠️</div>
|
|
<div class="module-error-text">${message}</div>
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
export default ModuleLoader;
|