Add pending apps and frontend components

- 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>
This commit is contained in:
ARCHITECT
2026-01-16 18:26:59 +00:00
parent 17506aaee2
commit 9b244138b5
177 changed files with 15063 additions and 0 deletions

View File

@@ -0,0 +1,174 @@
/**
* 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;