Manifest y UI
El archivo manifest.json en la raíz del plugin declara sus metadatos y su interfaz de usuario. Se valida contra la interfaz PluginManifest del SDK.
Estructura general
Sección titulada «Estructura general»interface PluginManifest { name: string; // id del plugin (kebab-case) version: string; // versión del propio plugin description?: string; author?: string; logo?: string; ui?: { routes?: PluginRoute[]; menuItems?: PluginMenuItem[]; // deprecated modules?: PluginModule[]; subTabs?: PluginSubTab[]; dashboardWidgets?: PluginDashboardWidget[]; themes?: PluginThemePreset[]; };}Ejemplo completo:
{ "name": "mi-plugin", "version": "1.0.0", "description": "Descripción corta del plugin", "author": "Tu Nombre", "ui": { "modules": [ { "id": "mi-plugin", "label": "Mi plugin", "icon": "Puzzle", "subTabs": [ { "label": "Panel", "path": "/plugin/mi-plugin", "icon": "LayoutDashboard" } ] } ], "routes": [ { "path": "/plugin/mi-plugin", "title": "Mi plugin", "type": "custom", "config": { "component": "ui/Page.tsx" } } ] }}ui.modules
Sección titulada «ui.modules»Un módulo top-level: aparece como icono nuevo en el sidebar del ERP.
interface PluginModule { id: string; // id único del módulo label: string; icon: string; // nombre de un icono de lucide-react (ej: "Tag", "Briefcase") subTabs?: Array<Omit<PluginSubTab, 'moduleId'>>; // pestañas propias del módulo}Los iconos son nombres de lucide-react ("Gift", "Truck", "BarChart3"…).
ui.subTabs
Sección titulada «ui.subTabs»Una pestaña horizontal inyectada en la topbar de un módulo existente (core o de otro plugin):
interface PluginSubTab { moduleId: string; // dónde inyectar: home | inventory | sales | purchases | // accounting | plugins | settings | <id de módulo de otro plugin> label: string; path: string; icon?: string; // icono lucide-react, opcional}{ "ui": { "subTabs": [ { "moduleId": "sales", "label": "Fidelización", "path": "/plugin/fidelizacion", "icon": "Gift" } ] }}ui.routes
Sección titulada «ui.routes»Cada ruta asocia un path del frontend con una vista:
interface PluginRoute { path: string; title: string; type: 'table' | 'form' | 'custom' | 'dashboard'; icon?: string; config?: any; // para type "custom": { "component": "ui/Page.tsx" }}custom— renderiza un componente React del plugin.config.componentes la ruta del archivo relativa a la raíz del plugin (ej."ui/Page.tsx"), que debe hacerexport defaultdel componente.table,form,dashboard— vistas generadas a partir deconfig.
ui.dashboardWidgets
Sección titulada «ui.dashboardWidgets»Widgets inyectados en el Dashboard principal del ERP:
interface PluginDashboardWidget { id: string; title: string; subtitle?: string; component: string; // ruta relativa al componente, ej. "ui/MiWidget.tsx" size?: 'sm' | 'md' | 'lg' | 'full'; // grid de 4 columnas: sm=1, md=2, lg=3, full=4 (default md) order?: number; // menor = antes (default 100)}La alternativa programática es context.widgets.registerDashboard(widget) desde init(), con el mismo shape — útil si el widget depende de lógica (por ejemplo, registrarlo solo bajo cierta condición). Ver PluginContext.
ui.themes
Sección titulada «ui.themes»Presets de tema que el plugin aporta al selector de Branding del ERP:
interface PluginThemePreset { id: string; label: string; description?: string; colorPrimary: string; // color CSS (hex, hsl...) colorAccent: string; themeMode: 'light' | 'dark';}{ "ui": { "themes": [ { "id": "mi-tema", "label": "Mi tema", "colorPrimary": "#7c3aed", "colorAccent": "#f59e0b", "themeMode": "dark" } ] }}ui.menuItems deprecated
Sección titulada «ui.menuItems »/** @deprecated Usa `modules` o `subTabs`. Se mantiene por compatibilidad. */interface PluginMenuItem { label: string; path: string; icon: string;}Era la forma original de añadir entradas de menú. Los menuItems existentes se mapean automáticamente como sub-tabs del módulo Plugins. Migra a:
modules— si tu plugin merece su propio icono en el sidebar.subTabs— si encaja como pestaña dentro de un módulo existente (sales,inventory…).